mini_kraken 0.3.01 → 0.3.02

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 184cc99bd7696eb4f51fea822e74c98f42396f92cd47e1ce4501bed4cbb6a3c0
4
- data.tar.gz: eb0bad624831f6643db7ec04265424cab58a9245f7fbfd472041e1eaa095de68
3
+ metadata.gz: aef8d438d839320f5a38af687cfef1e4018a687d5a9b3d071f5a485fa03cab3d
4
+ data.tar.gz: a8aefa5c850913634dbe5b28bec9c605782ef2ef7e8791632a9f7b504937442d
5
5
  SHA512:
6
- metadata.gz: 3803703fc02bda7cee5cd72f511f1a4a32bfecb85663571c1b15f002ab82b7551e49654aea9ce3a12e06ffa7c4839999c9ae4df9ce3fd2f9ba679ff827d47541
7
- data.tar.gz: 88bb32e2140526ee415542421ffad57ed768b67995c0a6d70b6c33d46f520b05ee312081b6d8a13ca64042ef004b0e7e2cf8baccbc226f385d0d67776220ac05
6
+ metadata.gz: 521fefadf00ba08606646afdd5f881a1a7bd4626221085c69adc2e822a2c2777b6fa28d71ea5f6541c614e0b8e9ca8470cab2301683d1c9f235bb1e3535f20f3
7
+ data.tar.gz: 99478f83ad99813aa29d82cef6cc5b8ad3d5df376d56819151ecc508d62f0a0fd23a62a1fb3553f8aa231e8c317ad5aa6930503b2cd8ff2951b6d94ca8b3480c
@@ -1,3 +1,13 @@
1
+ ## [0.3.02] - 2020-12-19
2
+ - `mini_kraken` now implements `conso` relation.
3
+
4
+ ### CHANGED
5
+ - Method `Context#build_solution` old source code (redundant) removed.
6
+ - File `README.md` minor changes.
7
+
8
+ ### FIXED
9
+ - Method `ConsCell#expand` pushed a node in the stack even if it was fully visited.
10
+
1
11
  ## [0.3.01] - 2020-12-17
2
12
  - Minor: updated Rubocop config file `.rubocop.yml`
3
13
 
data/README.md CHANGED
@@ -14,10 +14,10 @@ ISBN: 9780262535519, (2018), MIT Press.
14
14
  - Pure Ruby implementation, not a port from another language
15
15
  - Object-Oriented design
16
16
  - No runtime dependencies
17
- - Test suite patterned on the examples from the reference book.
17
+ - Test suite patterned on examples from the reference book.
18
18
 
19
19
  ### miniKanren Features
20
- - [X] ==
20
+ - [X] ( == ) unify
21
21
  - [X] run\*
22
22
  - [X] fresh
23
23
  - [X] conde
@@ -26,13 +26,13 @@ ISBN: 9780262535519, (2018), MIT Press.
26
26
  - [X] defrel
27
27
  - [X] caro
28
28
  - [X] cdro
29
+ - [X] conso
29
30
 
30
31
  ### TODO
31
32
 
32
33
  - [ ] Occurs check
33
34
 
34
- List-centric relations from Chapter 2
35
- - [ ] conso
35
+ Pair-centric relations from Chapter 2
36
36
  - [ ] nullo
37
37
  - [ ] pairo
38
38
  - [ ] singletono
@@ -179,7 +179,7 @@ module MiniKraken
179
179
  new_cell = ConsCell.null
180
180
  if curr_cell
181
181
  curr_cell.set!(side, new_cell)
182
- path.push(curr_cell)
182
+ path.push(curr_cell) unless side == :cdr
183
183
  end
184
184
  curr_cell = new_cell
185
185
  head ||= new_cell
@@ -239,138 +239,6 @@ module MiniKraken
239
239
  # Returns a Hash with pairs of the form:
240
240
  # { String => Association }, or
241
241
  # { String => AnyValue }
242
- def build_zolution
243
- clear_ranking
244
- calc_ranking
245
- solution = {}
246
- return solution if failure?
247
-
248
- # require 'debug'
249
- symbol_table.root.defns.each_pair do |nm, item|
250
- next unless item.kind_of?(LogVar)
251
-
252
- if failure?
253
- solution[nm] = nil
254
- next
255
- end
256
- i_name = item.i_name
257
- assocs = blackboard.associations_for(i_name, true)
258
- if assocs.nil? || assocs.empty? ||
259
- (blackboard.fused?(i_name) && assocs.empty?)
260
- solution[nm] = AnyValue.new(ranking[i_name])
261
- else
262
- my_assocs = []
263
- assocs.each { |a| my_assocs << a if a.kind_of?(Association) }
264
- next if my_assocs.empty?
265
-
266
- # TODO: if multiple associations, conj2 them...
267
- as = my_assocs.first
268
-
269
- deps = as.dependencies(self)
270
- if deps.any? { |i_name_dep| ranking.include? i_name_dep }
271
- # At least one dependent variable in expression is unbound
272
- solution[nm] = substitute(as)
273
- else
274
- solution[nm] = as.value
275
- end
276
- end
277
- end
278
- # Take into current scope (e.g. x).
279
- # e.g. if q depends on other inner scope variable,
280
- # then one should replace every occurrence of x by AnyValue
281
-
282
- solution
283
- end
284
- =begin
285
- find a solution for 1:67
286
- Scope picture:
287
- s_a:
288
- q
289
- ----
290
- s_b:
291
- x
292
- ----
293
- s_c:
294
- y
295
-
296
- Move_queue
297
- bk(s_b)
298
- bk(s_c)
299
- assoc x => :split
300
- assoc y => :pea
301
- assoc r => '(,x ,y)
302
-
303
- Let solution be a Hash i_name => value expression
304
- Let substitutions be a Hash i_name => value expression
305
- Start with root variables:
306
- For each root variable:
307
- Given q:
308
- Is it unbound? N
309
- Is it fused? N
310
- Get its association (Assumption: one association only)
311
- q => '( ,x ,y)
312
- dependents:
313
- Set { x, y}
314
- Given x:
315
- Is it unbound? N
316
- Is it fused? N
317
- Get its association
318
- x => :split
319
- no dependents => pinned, not a root variable:
320
- Add x => :split in substitution
321
- substitution = { x => :split }
322
- Given y:
323
- Is it unbound? N
324
- Is it fused? N
325
- Get its association
326
- y => :pea
327
- no dependents => pinned, not a root variable:
328
- Add y => :pea in substitution
329
- substitution = { x => :split, y => :pea }
330
- All dependents resolved? Y
331
- Add q => (:split :pea) to solution
332
- Done with root variables?
333
- Return solution: q => (split :pea)
334
-
335
- Let solution be a Hash i_name => value expression
336
- Let substitutions be a Hash i_name => value expression
337
- Method add_substitution_for(q, substitutions):
338
- Is it already present in substitutions, then return
339
-
340
- Is it unbound? N
341
- Is it fused? N
342
- Get its association (Assumption: one association only)
343
- Assume q => '( ,x ,y)
344
- with dependents: Set { x, y}
345
- foreach dependent variable call
346
- add_substitution_for(q, substitutions) # Recursive call
347
- no dependents => pinned
348
- Add association in substitution
349
-
350
- IDEA: placeholder in term expressions
351
- '( ,x ,y) is translated into:
352
- cons(placeholder_100(nil), placeholder_200(nil))
353
- x => [placeholder_100]
354
- y => [placeholder_200]
355
-
356
- class Placeholder {
357
- subj = nil
358
- def object_id ; subj.object_id ; end
359
- def kind_of? ; subj.kind_of? ; end
360
- def to_s ; subj.to_s ; end
361
- }
362
-
363
- Where are placedholders created?
364
- - In Association.new
365
-
366
- Do we really need Placeholder?
367
- Do LogVarRef not fill the bill?
368
- IDEA: at a given moment, a LogVarRef is instructed to refer to a value
369
- Difficulty: A variable can take multiple values
370
- Thus the logVarRef may not embed the value but refer to a value indexed by solution.
371
-
372
- =end
373
-
374
242
  def build_solution
375
243
  solution = {}
376
244
  return solution if failure?
@@ -19,7 +19,7 @@ module MiniKraken
19
19
  # When that object receives the message resume, it will
20
20
  # return a success context.
21
21
  # @param _actuals [Array] MUST be empty array for nullary relation.
22
- # @param _ctx [Core::Context] Runtime context
22
+ # @param ctx [Core::Context] Runtime context
23
23
  # @return [Core::DuckFiber]
24
24
  def solver_for(_actuals, ctx)
25
25
  # Important: every `solver_for` call will result in a distinct Context.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniKraken
4
- VERSION = '0.3.01'
4
+ VERSION = '0.3.02'
5
5
  end
@@ -31,11 +31,15 @@ module MiniKraken
31
31
  end
32
32
  end # context
33
33
 
34
- context 'Initialization:' do
34
+ context 'Provided services:' do
35
35
  def var(aName)
36
36
  LogVar.new(aName)
37
37
  end
38
38
 
39
+ def cons(term1, term2 = nil)
40
+ Composite::ConsCell.new(term1, term2)
41
+ end
42
+
39
43
  it 'should accept the addition of an entry in symbol table' do
40
44
  subject.insert(var('x'))
41
45
  expect(subject.symbol_table).not_to be_empty
@@ -189,7 +193,7 @@ module MiniKraken
189
193
  expect(subject.send(:substitute, assoc_q).to_s).to eq('(_0 :foo . :bar)')
190
194
  end
191
195
 
192
- it 'should build a solution' do
196
+ it 'should build a solution (atomic terms)' do
193
197
  subject.add_vars(%w[x y z])
194
198
 
195
199
  subject.succeeded!
@@ -215,6 +219,26 @@ module MiniKraken
215
219
  expect(sol['y']).to eq(bar)
216
220
  expect(sol['z']).to eq(:_0)
217
221
  end
222
+
223
+ it 'should build a solution (composite terms)' do
224
+ subject.add_vars(['l'])
225
+ new_scope = Scope.new
226
+ subject.enter_scope(new_scope)
227
+ a = k_symbol(:a)
228
+ b = k_symbol(:b)
229
+ c = k_symbol(:c)
230
+ subject.add_vars(['d'])
231
+ d_ref = LogVarRef.new('d')
232
+ subject.associate('l', cons(cons(a, cons(b)), d_ref))
233
+ subject.associate('d', cons(c))
234
+
235
+ subject.succeeded!
236
+ sol = subject.build_solution
237
+ expect(sol.size).to eq(1)
238
+
239
+ # TODO: fix next line. Actual is: ((a :c))
240
+ expect(sol['l'].to_s).to eq('((:a :b) :c)')
241
+ end
218
242
  end
219
243
  end # describe
220
244
  end # module
@@ -231,14 +231,14 @@ module MiniKraken
231
231
  # (cdro p d))
232
232
 
233
233
  # As 'p' has a special meaning in Ruby, the argument has been remaned to 'r'
234
- conso_rel = defrel('conso', %w[a r d], [caro(r, a), cdro(r, d)])
234
+ conso_rel = defrel('conso', %w[a d r], [caro(r, a), cdro(r, d)])
235
235
 
236
236
  expect(conso_rel).to be_kind_of(Rela::DefRelation)
237
237
  expect(conso_rel.name).to eq('conso')
238
238
  expect(conso_rel.arity).to eq(3)
239
239
  expect(conso_rel.formals[0]).to match(/^a_[-0-9a-f]+$/)
240
- expect(conso_rel.formals[1]).to match(/^r_[-0-9a-f]+$/)
241
- expect(conso_rel.formals[2]).to match(/^d_[-0-9a-f]+$/)
240
+ expect(conso_rel.formals[1]).to match(/^d_[-0-9a-f]+$/)
241
+ expect(conso_rel.formals[2]).to match(/^r_[-0-9a-f]+$/)
242
242
  g_template = conso_rel.expression
243
243
  expect(g_template.relation).to be_kind_of(Rela::Conj2)
244
244
  g1 = g_template.actuals[0]
@@ -255,7 +255,7 @@ module MiniKraken
255
255
  end
256
256
 
257
257
  # In Scheme:
258
- # (defrel (conso a p d)
258
+ # (defrel (conso a d p)
259
259
  # (caro p a)
260
260
  # (cdro p d))
261
261
  # In Ruby, `p`is a standard Kernel method => replace it by `r`
@@ -263,19 +263,87 @@ module MiniKraken
263
263
  defrel_caro
264
264
  defrel_cdro
265
265
 
266
- defrel('conso', %w[a r d], [caro(r, a), cdro(r, d)])
266
+ # Definition derived from frame 2:25
267
+ # defrel('conso', %w[a d r], [caro(r, a), cdro(r, d)])
268
+
269
+ # Definition derived from frame 2:26
270
+ defrel('conso', %w[a d r], [unify(cons(a, d), r)])
271
+ end
272
+
273
+ it 'passes frame 2:19' do
274
+ defrel_conso
275
+
276
+ # (run* l
277
+ # (conso '(a b c) '(d e) l)) ;; => (((a b c) d e))
278
+
279
+ result = run_star('l', conso(list(:a, :b, :c), list(:d, :e), l))
280
+ expect(result.to_s).to eq('(((:a :b :c) :d :e))')
281
+ end
282
+
283
+ it 'passes frame 2:20' do
284
+ defrel_conso
285
+
286
+ # (run* x
287
+ # (conso x '(a b c) '(d a b c))) ;; => (d)
288
+
289
+ result = run_star('x', conso(x, list(:a, :b, :c), list(:d, :a, :b, :c)))
290
+ expect(result.to_s).to eq('(:d)')
291
+ end
292
+
293
+ it 'passes frame 2:21' do
294
+ defrel_conso
295
+
296
+ # (run* r
297
+ # (fresh (x y z)
298
+ # (== '(e a d ,x) r)
299
+ # (conso y '(a ,z c) r))) ;; => ((e a d c)
300
+
301
+ expr = fresh(%w[x y z],
302
+ [unify(list(:e, :a, :d, x), r),
303
+ conso(y, list(:a, z, :c), r)])
304
+ result = run_star('r', expr)
305
+ expect(result.to_s).to eq('((:e :a :d :c))')
306
+ end
307
+
308
+ it 'passes frame 2:22' do
309
+ defrel_conso
310
+
311
+ # (run* x
312
+ # (conso x '(a ,x c) '(d a ,x c))) ;; => (d)
313
+
314
+ result = run_star('x', conso(x, list(:a, x, :c), list(:d, :a, x, :c)))
315
+ expect(result.to_s).to eq('(:d)')
267
316
  end
268
317
 
269
- # TODO: FIX THIS EXAMPLE
270
- # it 'passes frame 2:19' do
271
- # defrel_conso
318
+ it 'passes frame 2:23' do
319
+ defrel_conso
272
320
 
273
- # # (run* l
274
- # # (conso '(a b c) '(d e) l)) ;; => ((abc) (d e))
321
+ # (run* l
322
+ # (fresh (x)
323
+ # (== '(d a ,x c) l)
324
+ # (conso x '(a ,x c) l))) ;; => ((d a d c)
325
+
326
+ expr = fresh(%w[x],
327
+ [unify(list(:d, :a, x, :c), l),
328
+ conso(x, list(:a, x, :c), l)])
329
+ result = run_star('l', expr)
330
+ expect(result.to_s).to eq('((:d :a :d :c))')
331
+ end
332
+
333
+ it 'passes frame 2:24' do
334
+ defrel_conso
275
335
 
276
- # result = run_star('l', conso( list(:a, :b, :c), list(:d, :e), l))
277
- # expect(result.to_s).to eq('((:a :b :c) (:d :e))')
278
- # end
336
+ # (run* l
337
+ # (fresh (x)
338
+ # (conso x '(a ,x c) l)))
339
+ # (== '(d a ,x c) l) ;; => ((d a d c)
340
+
341
+ expr = fresh(%w[x],
342
+ [conso(x, list(:a, x, :c), l),
343
+ unify(list(:d, :a, x, :c), l)])
344
+ result = run_star('l', expr)
345
+ expect(result.to_s).to eq('((:d :a :d :c))')
346
+ end
279
347
  end # context
280
348
  end # describe
281
349
  end # module
@@ -80,7 +80,7 @@ module MiniKraken
80
80
 
81
81
  # @return [Core::Goal]
82
82
  def fresh(names, subgoal)
83
- puts "#{__callee__} #{names}"
83
+ # puts "#{__callee__} #{names}"
84
84
  if names.kind_of?(Array)
85
85
  k_names = names.map { |nm| Atomic::KString.new(nm) }
86
86
  else
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_kraken
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.01
4
+ version: 0.3.02
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-17 00:00:00.000000000 Z
11
+ date: 2020-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler