rubylog 2.0.0 → 2.0.1
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.
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/README.rdoc +6 -3
- data/RELEASE_NOTES.rdoc +5 -1
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/examples/benchmark/compiled_not_indexed.rb +32 -0
- data/examples/benchmark/compiled_sequence_indexed.rb +47 -0
- data/examples/benchmark/indexed_procedure.rb +57 -0
- data/examples/benchmark/prolog.rb +39 -0
- data/examples/benchmark/pure.rb +26 -0
- data/examples/benchmark.rb +113 -0
- data/examples/divisors.rb +1 -3
- data/examples/factorial.rb +3 -2
- data/examples/file_search.rb +9 -7
- data/examples/hanoi.rb +14 -15
- data/examples/hu/csaladfa.rb +18 -0
- data/examples/mice2.rb +29 -11
- data/examples/sudoku.rb +10 -45
- data/lib/rubylog/builtins/file_system.rb +1 -1
- data/rubylog.gemspec +13 -5
- data/spec/rubylog/builtins/file_system_spec.rb +3 -0
- data/spec/rubylog/variable_spec.rb +14 -0
- metadata +31 -16
- data/README.rdoc.orig +0 -284
- data/spec/integration/recursion_spec.rb +0 -14
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -21,6 +21,7 @@ GEM
|
|
21
21
|
rspec-expectations (2.13.0)
|
22
22
|
diff-lcs (>= 1.1.3, < 2.0)
|
23
23
|
rspec-mocks (2.13.1)
|
24
|
+
ruby-prof (0.13.0)
|
24
25
|
simplecov (0.7.1)
|
25
26
|
multi_json (~> 1.0)
|
26
27
|
simplecov-html (~> 0.7.1)
|
@@ -34,5 +35,6 @@ DEPENDENCIES
|
|
34
35
|
bundler (>= 1.0.0)
|
35
36
|
jeweler (>= 1.8.3)
|
36
37
|
rspec (>= 2.8.0, < 3)
|
38
|
+
ruby-prof
|
37
39
|
simplecov
|
38
40
|
yard (~> 0.7)
|
data/README.rdoc
CHANGED
@@ -33,7 +33,6 @@ Another option is to use Kernel#rubylog:
|
|
33
33
|
|
34
34
|
|
35
35
|
require 'rubylog'
|
36
|
-
extend Rubylog::Context
|
37
36
|
|
38
37
|
rubylog do
|
39
38
|
# your code here
|
@@ -84,7 +83,7 @@ Declaring a predicate with arguments gives you three methods on the subject clas
|
|
84
83
|
|
85
84
|
predicate_for String, ".likes()"
|
86
85
|
'John'.likes('beer') # returns a structure object representing this logical statement
|
87
|
-
'John'.likes!('beer') # asserts
|
86
|
+
'John'.likes!('beer') # asserts this statement as a fact
|
88
87
|
'John'.likes?('beer') # tells if this statement is true (in this case, returns true)
|
89
88
|
|
90
89
|
=== Nullary predicates
|
@@ -271,7 +270,7 @@ These predicates also have a prefix form, which can be used to create more natur
|
|
271
270
|
check none X.in([1,3]), X.even
|
272
271
|
check iff X.in([2,4]), X.in(1..4).and(X.even)
|
273
272
|
|
274
|
-
There is another quantifier
|
273
|
+
There is another quantifier <tt>A.every(B)</tt> or <tt>every(A,B)</tt>. This works similarly to <tt>.all()</tt>, but for each solution of A, creates a copy of B and chains them together with <tt>.and()</tt>. It can be useful for work with assumptions, see below. This is an experimental feature, and still contains bugs.
|
275
274
|
|
276
275
|
=== File system
|
277
276
|
|
@@ -335,6 +334,10 @@ You can turn on tracing with
|
|
335
334
|
|
336
335
|
Rubylog.trace
|
337
336
|
|
337
|
+
And turn off with
|
338
|
+
|
339
|
+
Rubylog.trace false
|
340
|
+
|
338
341
|
Or, you can trace a specific code block with
|
339
342
|
|
340
343
|
Rubylog.trace do
|
data/RELEASE_NOTES.rdoc
CHANGED
@@ -4,7 +4,11 @@
|
|
4
4
|
* warning for singleton variables
|
5
5
|
* predicate for checking bound/unbound variables
|
6
6
|
|
7
|
-
== Version 2.0.
|
7
|
+
== Version 2.0.1
|
8
|
+
* Bug fixes
|
9
|
+
* <tt>"a/b".file\_in("a")</tt> only succeeds once
|
10
|
+
|
11
|
+
== Version 2.0.0
|
8
12
|
* Backwards incompatibilities
|
9
13
|
* <tt>Rubylog.theory</tt> renamed to <tt>Kernel#rubylog</tt>.
|
10
14
|
* <tt>file\_in</tt> includes directories and yields relative paths.
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0.
|
1
|
+
2.0.1
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require "rubylog"
|
3
|
+
extend Rubylog::Context
|
4
|
+
|
5
|
+
|
6
|
+
class << primitives
|
7
|
+
def self.make_tree(parent, levels, s="")
|
8
|
+
return if levels.zero?
|
9
|
+
|
10
|
+
children = (1..DEGREES).map{random_person}
|
11
|
+
|
12
|
+
s << "a.rubylog_unify(#{parent.inspect}){\n"
|
13
|
+
children.each do |child|
|
14
|
+
s << "b.rubylog_unify(#{child.inspect}){yield}\n"
|
15
|
+
end
|
16
|
+
s << "}\n"
|
17
|
+
children.each do |child|
|
18
|
+
make_tree(child, levels-1, s)
|
19
|
+
end
|
20
|
+
s
|
21
|
+
end
|
22
|
+
|
23
|
+
eval "def parent_of a,b
|
24
|
+
#{make_tree(random_person, LEVELS)}
|
25
|
+
end"
|
26
|
+
|
27
|
+
def grandparent_of a,b
|
28
|
+
x = Rubylog::Variable.new
|
29
|
+
parent_of(a,x) { parent_of(x,b) { yield }}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require "rubylog"
|
3
|
+
extend Rubylog::Context
|
4
|
+
|
5
|
+
|
6
|
+
class << primitives
|
7
|
+
def self.make_tree(parent, levels, all="", indexed= "")
|
8
|
+
return if levels.zero?
|
9
|
+
|
10
|
+
children = (1..DEGREES).map{random_person}
|
11
|
+
|
12
|
+
all << "a.rubylog_unify(#{parent.inspect}){\n"
|
13
|
+
children.each do |child|
|
14
|
+
all << "b.rubylog_unify(#{child.inspect}){yield}\n"
|
15
|
+
end
|
16
|
+
all << "}\n"
|
17
|
+
|
18
|
+
indexed << "when #{parent.inspect}\n"
|
19
|
+
children.each do |child|
|
20
|
+
indexed << "b.rubylog_unify(#{child.inspect}){yield}\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
children.each do |child|
|
24
|
+
make_tree(child, levels-1, all, indexed)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
all =""; indexed =""
|
30
|
+
make_tree(random_person, LEVELS, all, indexed)
|
31
|
+
|
32
|
+
eval "def parent_of a,b
|
33
|
+
a = a.rubylog_dereference
|
34
|
+
case a
|
35
|
+
when Rubylog::Variable
|
36
|
+
#{all}
|
37
|
+
#{indexed}
|
38
|
+
end
|
39
|
+
end"
|
40
|
+
|
41
|
+
def grandparent_of a,b
|
42
|
+
x = Rubylog::Variable.new
|
43
|
+
parent_of(a,x) { parent_of(x,b) { yield }}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class Rubylog::Procedure
|
2
|
+
def initialize functor, arity, rules=Array.new
|
3
|
+
super functor, arity
|
4
|
+
@rules = rules
|
5
|
+
@index = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def each(args)
|
9
|
+
index = args[0].rubylog_dereference
|
10
|
+
if !index.is_a? Rubylog::Variable
|
11
|
+
(@index[index] || []).each do |rule|
|
12
|
+
yield rule
|
13
|
+
end
|
14
|
+
else
|
15
|
+
@rules.each do |rule|
|
16
|
+
yield rule
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
rubylog_traceable :each
|
21
|
+
|
22
|
+
# accepts the *args of the called structure
|
23
|
+
def call *args
|
24
|
+
# catch cuts
|
25
|
+
catch :rubylog_cut do
|
26
|
+
|
27
|
+
# for each rule
|
28
|
+
each(args) do |rule|
|
29
|
+
# compile
|
30
|
+
rule = rule.rubylog_match_variables
|
31
|
+
|
32
|
+
# unify the head with the arguments
|
33
|
+
rule.head.args.rubylog_unify(args) do
|
34
|
+
# call the body
|
35
|
+
rule.body.prove do
|
36
|
+
yield
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
rubylog_traceable :call
|
43
|
+
|
44
|
+
# Asserts a rule with a given head and body, indexed
|
45
|
+
def assert head, body = :true
|
46
|
+
rule = Rubylog::Rule.new(head, body)
|
47
|
+
|
48
|
+
# update index
|
49
|
+
index = head[0].rubylog_dereference
|
50
|
+
if !index.is_a? Rubylog::Variable
|
51
|
+
(@index[index] ||= []).push rule
|
52
|
+
end
|
53
|
+
|
54
|
+
@rules.push rule
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
|
4
|
+
puts "Native prolog"
|
5
|
+
|
6
|
+
def make_prolog_tree(parent, levels, s="")
|
7
|
+
return if levels.zero?
|
8
|
+
|
9
|
+
children = (1..DEGREES).map{random_person}
|
10
|
+
|
11
|
+
children.each do |child|
|
12
|
+
s << "parent_of(#{parent},#{child}).\n"
|
13
|
+
end
|
14
|
+
children.each do |child|
|
15
|
+
make_prolog_tree(child, levels-1, s)
|
16
|
+
end
|
17
|
+
s
|
18
|
+
end
|
19
|
+
|
20
|
+
s = make_prolog_tree(random_person, LEVELS)
|
21
|
+
s << <<EOT
|
22
|
+
grandparent_of(A,B) :- parent_of(A,X), parent_of(X,B).
|
23
|
+
|
24
|
+
:- dynamic(result/1).
|
25
|
+
|
26
|
+
main :- findall([A,B],grandparent_of(A,B),L), assertz(result(L)), halt.
|
27
|
+
|
28
|
+
:- initialization(main).
|
29
|
+
EOT
|
30
|
+
|
31
|
+
File.open(File.expand_path(__FILE__+"/../prolog.pl"), "w") do |f|
|
32
|
+
f << s
|
33
|
+
end
|
34
|
+
|
35
|
+
system 'gplc ./examples/benchmark/prolog.pl'
|
36
|
+
system 'bash -c "time ./examples/benchmark/prolog"'
|
37
|
+
|
38
|
+
|
39
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require "rubylog"
|
3
|
+
extend Rubylog::Context
|
4
|
+
|
5
|
+
predicate_for $person_class, ".parent_of() .grandparent_of()"
|
6
|
+
|
7
|
+
def make_tree(parent, levels)
|
8
|
+
return if levels.zero?
|
9
|
+
|
10
|
+
children = (1..DEGREES).map{random_person}
|
11
|
+
|
12
|
+
children.each do |child|
|
13
|
+
# add relationship
|
14
|
+
parent.parent_of!(child)
|
15
|
+
end
|
16
|
+
|
17
|
+
children.each do |child|
|
18
|
+
# make sub-tree
|
19
|
+
make_tree(child, levels-1)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
make_tree(random_person, LEVELS)
|
24
|
+
|
25
|
+
A.grandparent_of(B).if A.parent_of(X).and X.parent_of(B)
|
26
|
+
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# benchmark
|
2
|
+
#
|
3
|
+
# The task is to collect all grandparent-granchild relationships in a family
|
4
|
+
# tree to an array.
|
5
|
+
# First it is done with pure rubylog. Second, it is done with compiled rubylog
|
6
|
+
#
|
7
|
+
#
|
8
|
+
|
9
|
+
require "rubylog"
|
10
|
+
require "benchmark"
|
11
|
+
require "ruby-prof"
|
12
|
+
|
13
|
+
|
14
|
+
DEGREES = 3
|
15
|
+
LEVELS = 6
|
16
|
+
NAME_LENGTH = 5
|
17
|
+
|
18
|
+
class Person
|
19
|
+
def initialize name
|
20
|
+
@name = name
|
21
|
+
end
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
# this is a quick solution for compiled*.rb code to retrieve the object
|
25
|
+
"ObjectSpace._id2ref(#{object_id})"
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
# this is needed for comparing results
|
30
|
+
@name
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# create a random person
|
35
|
+
def random_person
|
36
|
+
# create random name
|
37
|
+
name = ""
|
38
|
+
NAME_LENGTH.times { name << (97+$random.rand(26)).chr}
|
39
|
+
name
|
40
|
+
|
41
|
+
if $person_class == String
|
42
|
+
name
|
43
|
+
elsif $person_class == Symbol
|
44
|
+
name.to_sym
|
45
|
+
elsif $person_class = Person
|
46
|
+
Person.new(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def run_benchmark(id, person_class, name)
|
51
|
+
$person_class = person_class
|
52
|
+
$random = Random.new(1)
|
53
|
+
|
54
|
+
load "./examples/benchmark/#{id}.rb"
|
55
|
+
|
56
|
+
puts name
|
57
|
+
|
58
|
+
result = nil
|
59
|
+
time = Benchmark.realtime {
|
60
|
+
result = rubylog do
|
61
|
+
A.grandparent_of(B).map do
|
62
|
+
[A.to_s,B.to_s]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
}
|
66
|
+
puts "%0.5f sec" % time
|
67
|
+
|
68
|
+
|
69
|
+
# forget result
|
70
|
+
result = result.inspect
|
71
|
+
|
72
|
+
# compare with last result
|
73
|
+
raise "#{$last_result} != #{result}" if $last_result && $last_result != result
|
74
|
+
$last_result = result
|
75
|
+
|
76
|
+
# make garbage collection
|
77
|
+
GC.start; sleep(time*1.2+1)
|
78
|
+
end
|
79
|
+
|
80
|
+
puts "Rubylog Grandparent Benchmark"
|
81
|
+
puts
|
82
|
+
puts "DEGREES = #{DEGREES}"
|
83
|
+
puts "LEVELS = #{LEVELS}"
|
84
|
+
puts "NAME_LENGTH = #{NAME_LENGTH}"
|
85
|
+
puts "NODES = #{DEGREES**LEVELS}"
|
86
|
+
puts
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
run_benchmark "pure", String, "Strings"
|
91
|
+
run_benchmark "pure", Person, "Objects"
|
92
|
+
run_benchmark "pure", Symbol, "Symbols"
|
93
|
+
puts
|
94
|
+
|
95
|
+
require "./examples/benchmark/indexed_procedure"
|
96
|
+
|
97
|
+
run_benchmark "pure", String, "Strings indexed"
|
98
|
+
run_benchmark "pure", Person, "Objects indexed"
|
99
|
+
run_benchmark "pure", Symbol, "Symbols indexed"
|
100
|
+
puts
|
101
|
+
|
102
|
+
run_benchmark "compiled_not_indexed", String, "Strings compiled"
|
103
|
+
run_benchmark "compiled_not_indexed", Person, "Objects compiled"
|
104
|
+
run_benchmark "compiled_not_indexed", Symbol, "Symbols compiled"
|
105
|
+
puts
|
106
|
+
|
107
|
+
run_benchmark "compiled_sequence_indexed", String, "Strings compiled, sequentially indexed"
|
108
|
+
#run_benchmark "compiled_sequence_indexed", Person, "Objects compiled, sequentially indexed"
|
109
|
+
run_benchmark "compiled_sequence_indexed", Symbol, "Symbols compiled, sequentially indexed"
|
110
|
+
puts
|
111
|
+
|
112
|
+
# native prolog
|
113
|
+
load "./examples/benchmark/prolog.rb"
|
data/examples/divisors.rb
CHANGED
data/examples/factorial.rb
CHANGED
data/examples/file_search.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
require "rubylog"
|
2
2
|
extend Rubylog::Context
|
3
3
|
|
4
|
-
|
4
|
+
[true,false].each do |b|
|
5
|
+
puts "Files which #{b ? 'have' : 'do not have'} spec:"
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
"lib/#{X}.rb".file_in("lib/**").each do
|
8
|
+
if b == "spec/#{X}_spec.rb".file_in?("spec/**")
|
9
|
+
puts X
|
10
|
+
end
|
11
|
+
end
|
8
12
|
|
9
|
-
|
10
|
-
"#{X}/spec/#{S}_spec.rb".found_in("spec").each do
|
11
|
-
puts S
|
12
|
-
end
|
13
|
+
puts
|
13
14
|
|
15
|
+
end
|
14
16
|
|
data/examples/hanoi.rb
CHANGED
@@ -1,23 +1,22 @@
|
|
1
1
|
$:.unshift File.dirname(__FILE__)+"/../lib"
|
2
2
|
require "rubylog"
|
3
|
+
extend Rubylog::Context
|
3
4
|
|
4
|
-
|
5
|
-
predicate_for Integer, ".move(,,)", ".hanoi"
|
5
|
+
predicate_for Integer, ".move(,,)", ".hanoi"
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
0.move(ANY,ANY,ANY).if :cut!
|
8
|
+
N.move(A,B,C).if (
|
9
|
+
M.is{N-1}.and \
|
10
|
+
M.move(A,C,B).and \
|
11
|
+
{ puts "move disc #{N} from the #{A} pole to the #{B} pole" or true }.and \
|
12
|
+
M.move(C,B,A)
|
13
|
+
)
|
14
|
+
N.hanoi.if N.move('left', 'right', 'center')
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
puts "\nWhat's the solution for a single disc?"
|
17
|
+
solve 1.hanoi
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
puts "\n\nWhat's the solution for 5 discs?"
|
20
|
+
solve 5.hanoi
|
21
21
|
|
22
22
|
|
23
|
-
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require "rubylog"
|
3
|
+
extend Rubylog::Context
|
4
|
+
|
5
|
+
predicate_for String, ".parent_of() .grandparent_of()"
|
6
|
+
|
7
|
+
# ki kinek a szülője
|
8
|
+
'Bob'.parent_of!('Greg')
|
9
|
+
'Greg'.parent_of!('John')
|
10
|
+
'Greg'.parent_of!('Jane')
|
11
|
+
|
12
|
+
# "A" nagyszülője B-nek, ha "A" szülője X-nek és X szülője B-nek
|
13
|
+
A.grandparent_of(B).if A.parent_of(X).and X.parent_of(B)
|
14
|
+
|
15
|
+
# Az összes nagyszülő-unoka kapcsolat kiírása
|
16
|
+
A.grandparent_of(B).each do
|
17
|
+
puts "#{A} unokája #{B}."
|
18
|
+
end
|
data/examples/mice2.rb
CHANGED
@@ -1,19 +1,37 @@
|
|
1
1
|
require "rubylog"
|
2
2
|
extend Rubylog::Context
|
3
3
|
|
4
|
-
predicate_for
|
4
|
+
predicate_for Rubylog::Variable, ".bound"
|
5
|
+
A.bound.if { A.is_a? Rubylog::Variable }
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
class Array
|
8
|
+
extend Rubylog::Context
|
8
9
|
|
9
|
-
|
10
|
-
[
|
11
|
-
|
10
|
+
predicate ".can_have_neighbours"
|
11
|
+
[*ANY,:yes,:yes,*ANY].can_have_neighbours!
|
12
|
+
|
13
|
+
predicate ".has_neighbours"
|
14
|
+
[*ANY,A,B,*ANY].has_neighbours.if { A == :yes && B == :yes}
|
15
|
+
|
16
|
+
predicate ".cannot_have_neighbours"
|
17
|
+
A.cannot_have_neighbours.if A.can_have_neighbours.false
|
18
|
+
|
19
|
+
predicate ".decidable"
|
20
|
+
A.decidable.if A.has_neighbours.or A.cannot_have_neighbours
|
21
|
+
|
22
|
+
|
23
|
+
predicate ".guessed"
|
24
|
+
A.guessed.if every X.in(A), X.is(:yes).or(X.is(:no)).or(:true)
|
25
|
+
|
26
|
+
predicate ".easy"
|
27
|
+
A.easy.if A.guessed.and A.decidable.and any X.in(A), X.bound.false
|
28
|
+
|
29
|
+
Rubylog.trace
|
30
|
+
N.in(0..6).each do
|
31
|
+
puts "#{N}: #{([ANY]*N).easy?}"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
12
35
|
|
13
|
-
[].guess!([])
|
14
|
-
[nil,*REST].guess([A,*REST2]).if A.in([0,1]).and REST.guess(REST2)
|
15
|
-
[0,*REST].guess([0,*REST2]).if A.in([0,1]).and REST.guess(REST2)
|
16
|
-
[1,*REST].guess([1,*REST2]).if A.in([0,1]).and REST.guess(REST2)
|
17
36
|
|
18
|
-
solve N.is(6).and any Peek.peek(N), all(Peek.seen(Seen), all(Guess.guess(Seen), Guess.has_neighbors.false).or(all(Guess.guess(Seen), Guess.has_neighbors)))
|
19
37
|
|
data/examples/sudoku.rb
CHANGED
@@ -1,52 +1,17 @@
|
|
1
1
|
$:.unshift File.dirname(__FILE__)+"/../lib"
|
2
2
|
require "rubylog"
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
attr_reader :rows
|
10
|
-
|
11
|
-
def columns
|
12
|
-
@rows.transpose
|
13
|
-
end
|
14
|
-
|
15
|
-
def blocks
|
16
|
-
(0..2).map {|a| (0..2).map {|b| (0..2).map {|c| (0..2).map {|d|
|
17
|
-
@rows[a*3+c][b*3+d]
|
18
|
-
}}.flatten(1) }}.flatten(1)
|
19
|
-
end
|
20
|
-
|
21
|
-
extend Rubylog::Context
|
22
|
-
|
23
|
-
predicate_for Array, ".unique"
|
24
|
-
L.unique.unless L.is [*ANY, X, *ANY, X, *ANY]
|
25
|
-
|
26
|
-
predicate ".solved .good .shown .given(s)"
|
27
|
-
S.solved.if every L.in{S.rows}.and(F.in(L)), F.in(1..9)
|
28
|
-
S.good.if all(T.in{[S.rows, S.columns, S.blocks]}.and(L.in{T}), L.unique)
|
4
|
+
def blocks_of s
|
5
|
+
(0..2).map {|a| (0..2).map {|b| (0..2).map {|c| (0..2).map {|d|
|
6
|
+
s[a*3+c][b*3+d]
|
7
|
+
}}.flatten(1) }}.flatten(1)
|
8
|
+
end
|
29
9
|
|
30
|
-
|
31
|
-
L.in{S.rows}.each do
|
32
|
-
p L
|
33
|
-
end
|
34
|
-
puts
|
35
|
-
true
|
36
|
-
end
|
10
|
+
predicate_for Array, ".sudoku .row_unique .col_unique .block_unique .unique"
|
37
11
|
|
38
|
-
|
39
|
-
solve S.is{Sudoku.new}.and lambda{S.rows}.is([
|
40
|
-
[5,_,_,_,2,4,7,_,_],
|
41
|
-
[_,_,2,_,_,_,8,_,_],
|
42
|
-
[1,_,_,7,_,3,9,_,2],
|
43
|
-
[_,_,8,_,7,2,_,4,9],
|
44
|
-
[_,2,_,9,8,_,_,7,_],
|
45
|
-
[7,9,_,_,_,_,_,8,_],
|
46
|
-
[_,_,_,_,3,_,5,_,6],
|
47
|
-
[9,6,_,_,1,_,3,_,_],
|
48
|
-
[_,5,_,6,9,_,_,1,_]
|
49
|
-
]).and S.solved.and S.good.and S.shown rescue p $!
|
50
|
-
end
|
12
|
+
S.sudoku.if S.is([[ANY]*9]*9).and S.row_unique.and S.col_unique.and S.block_unique.and every(ROW.in(S).and(X.in(ROW)), X.in(1..9))
|
51
13
|
|
14
|
+
S.row_unique.if every ROW.in(S), ROW.unique
|
15
|
+
S.col_unique.if every COL.in{S.transpose}, COL.unique
|
16
|
+
S.col_unique.if every BLOCK.in{blocks_of(S)}, BLOCK.unique
|
52
17
|
|
@@ -14,7 +14,7 @@ rubylog do
|
|
14
14
|
File.basename(x) !~ /\A\.\.?\z/
|
15
15
|
}
|
16
16
|
}
|
17
|
-
X.file_in(D).if proc{! X.is_a? Rubylog::Variable and File.exist?(X)}.and D.is { File.dirname(X) }
|
17
|
+
X.file_in(D).if proc{! X.is_a? Rubylog::Variable and D.is_a?(Rubylog::Variable) and File.exist?(X)}.and D.is { File.dirname(X) }
|
18
18
|
|
19
19
|
# If D is not a variable, succeeds if X unifies with a directory in directory D. X
|
20
20
|
# necessarily starts with "#{D}/". X can contain variables, D cannot.
|
data/rubylog.gemspec
CHANGED
@@ -5,17 +5,16 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "rubylog"
|
8
|
-
s.version = "2.0.
|
8
|
+
s.version = "2.0.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Bern\u{e1}t Kall\u{f3}"]
|
12
|
-
s.date = "2013-05-
|
12
|
+
s.date = "2013-05-16"
|
13
13
|
s.description = "Rubylog is a Prolog-like DSL for Ruby."
|
14
14
|
s.email = "kallo.bernat@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE.txt",
|
17
|
-
"README.rdoc"
|
18
|
-
"README.rdoc.orig"
|
17
|
+
"README.rdoc"
|
19
18
|
]
|
20
19
|
s.files = [
|
21
20
|
".document",
|
@@ -29,6 +28,12 @@ Gem::Specification.new do |s|
|
|
29
28
|
"TODO.txt",
|
30
29
|
"VERSION",
|
31
30
|
"examples/a_plus_b.rb",
|
31
|
+
"examples/benchmark.rb",
|
32
|
+
"examples/benchmark/compiled_not_indexed.rb",
|
33
|
+
"examples/benchmark/compiled_sequence_indexed.rb",
|
34
|
+
"examples/benchmark/indexed_procedure.rb",
|
35
|
+
"examples/benchmark/prolog.rb",
|
36
|
+
"examples/benchmark/pure.rb",
|
32
37
|
"examples/checkmate.rb",
|
33
38
|
"examples/combination.rb",
|
34
39
|
"examples/dcg.rb",
|
@@ -41,6 +46,7 @@ Gem::Specification.new do |s|
|
|
41
46
|
"examples/file_search.rb",
|
42
47
|
"examples/hanoi.rb",
|
43
48
|
"examples/hello.rb",
|
49
|
+
"examples/hu/csaladfa.rb",
|
44
50
|
"examples/mice.rb",
|
45
51
|
"examples/mice2.rb",
|
46
52
|
"examples/n_queens.rb",
|
@@ -170,7 +176,6 @@ Gem::Specification.new do |s|
|
|
170
176
|
"spec/inriasuite_spec.rb",
|
171
177
|
"spec/integration/custom_classes_spec.rb",
|
172
178
|
"spec/integration/dsl_spec.rb",
|
173
|
-
"spec/integration/recursion_spec.rb",
|
174
179
|
"spec/integration/theory_as_module_spec.rb",
|
175
180
|
"spec/integration/theory_as_module_with_include_spec.rb",
|
176
181
|
"spec/rspec/rubylog_spec.rb",
|
@@ -215,12 +220,14 @@ Gem::Specification.new do |s|
|
|
215
220
|
s.specification_version = 3
|
216
221
|
|
217
222
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
223
|
+
s.add_development_dependency(%q<ruby-prof>, [">= 0"])
|
218
224
|
s.add_development_dependency(%q<rspec>, ["< 3", ">= 2.8.0"])
|
219
225
|
s.add_development_dependency(%q<yard>, ["~> 0.7"])
|
220
226
|
s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
|
221
227
|
s.add_development_dependency(%q<jeweler>, [">= 1.8.3"])
|
222
228
|
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
223
229
|
else
|
230
|
+
s.add_dependency(%q<ruby-prof>, [">= 0"])
|
224
231
|
s.add_dependency(%q<rspec>, ["< 3", ">= 2.8.0"])
|
225
232
|
s.add_dependency(%q<yard>, ["~> 0.7"])
|
226
233
|
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
@@ -228,6 +235,7 @@ Gem::Specification.new do |s|
|
|
228
235
|
s.add_dependency(%q<simplecov>, [">= 0"])
|
229
236
|
end
|
230
237
|
else
|
238
|
+
s.add_dependency(%q<ruby-prof>, [">= 0"])
|
231
239
|
s.add_dependency(%q<rspec>, ["< 3", ">= 2.8.0"])
|
232
240
|
s.add_dependency(%q<yard>, ["~> 0.7"])
|
233
241
|
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
@@ -145,6 +145,20 @@ describe Rubylog::Variable, :rubylog=>true do
|
|
145
145
|
check 3.is(Rubylog::Variable.new(:var1 )).and(4.is(Rubylog::Variable.new(:var1 ))).false
|
146
146
|
end
|
147
147
|
|
148
|
+
describe "variables support recursion" do
|
149
|
+
predicate_for Integer, ".factorial()"
|
150
|
+
0.factorial! 1
|
151
|
+
N[thats > 0].factorial(K).if N1.is {N-1} .and N1.factorial(K1).and K.is{ N*K1 }
|
152
|
+
|
153
|
+
check 0.factorial 1
|
154
|
+
check 1.factorial 1
|
155
|
+
check 2.factorial 2
|
156
|
+
check 3.factorial 6
|
157
|
+
check 4.factorial 24
|
158
|
+
check 7.factorial 5040
|
159
|
+
end
|
160
|
+
|
161
|
+
|
148
162
|
describe "dont-care variables support recursion" do
|
149
163
|
predicate_for Integer, ".factorial()"
|
150
164
|
0.factorial! 1
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubylog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-16 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ruby-prof
|
16
|
+
requirement: &79320110 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *79320110
|
14
25
|
- !ruby/object:Gem::Dependency
|
15
26
|
name: rspec
|
16
|
-
requirement: &
|
27
|
+
requirement: &79319690 !ruby/object:Gem::Requirement
|
17
28
|
none: false
|
18
29
|
requirements:
|
19
30
|
- - <
|
@@ -24,10 +35,10 @@ dependencies:
|
|
24
35
|
version: 2.8.0
|
25
36
|
type: :development
|
26
37
|
prerelease: false
|
27
|
-
version_requirements: *
|
38
|
+
version_requirements: *79319690
|
28
39
|
- !ruby/object:Gem::Dependency
|
29
40
|
name: yard
|
30
|
-
requirement: &
|
41
|
+
requirement: &79319150 !ruby/object:Gem::Requirement
|
31
42
|
none: false
|
32
43
|
requirements:
|
33
44
|
- - ~>
|
@@ -35,10 +46,10 @@ dependencies:
|
|
35
46
|
version: '0.7'
|
36
47
|
type: :development
|
37
48
|
prerelease: false
|
38
|
-
version_requirements: *
|
49
|
+
version_requirements: *79319150
|
39
50
|
- !ruby/object:Gem::Dependency
|
40
51
|
name: bundler
|
41
|
-
requirement: &
|
52
|
+
requirement: &79318590 !ruby/object:Gem::Requirement
|
42
53
|
none: false
|
43
54
|
requirements:
|
44
55
|
- - ! '>='
|
@@ -46,10 +57,10 @@ dependencies:
|
|
46
57
|
version: 1.0.0
|
47
58
|
type: :development
|
48
59
|
prerelease: false
|
49
|
-
version_requirements: *
|
60
|
+
version_requirements: *79318590
|
50
61
|
- !ruby/object:Gem::Dependency
|
51
62
|
name: jeweler
|
52
|
-
requirement: &
|
63
|
+
requirement: &79318130 !ruby/object:Gem::Requirement
|
53
64
|
none: false
|
54
65
|
requirements:
|
55
66
|
- - ! '>='
|
@@ -57,10 +68,10 @@ dependencies:
|
|
57
68
|
version: 1.8.3
|
58
69
|
type: :development
|
59
70
|
prerelease: false
|
60
|
-
version_requirements: *
|
71
|
+
version_requirements: *79318130
|
61
72
|
- !ruby/object:Gem::Dependency
|
62
73
|
name: simplecov
|
63
|
-
requirement: &
|
74
|
+
requirement: &79317710 !ruby/object:Gem::Requirement
|
64
75
|
none: false
|
65
76
|
requirements:
|
66
77
|
- - ! '>='
|
@@ -68,7 +79,7 @@ dependencies:
|
|
68
79
|
version: '0'
|
69
80
|
type: :development
|
70
81
|
prerelease: false
|
71
|
-
version_requirements: *
|
82
|
+
version_requirements: *79317710
|
72
83
|
description: Rubylog is a Prolog-like DSL for Ruby.
|
73
84
|
email: kallo.bernat@gmail.com
|
74
85
|
executables: []
|
@@ -76,7 +87,6 @@ extensions: []
|
|
76
87
|
extra_rdoc_files:
|
77
88
|
- LICENSE.txt
|
78
89
|
- README.rdoc
|
79
|
-
- README.rdoc.orig
|
80
90
|
files:
|
81
91
|
- .document
|
82
92
|
- .rspec
|
@@ -89,6 +99,12 @@ files:
|
|
89
99
|
- TODO.txt
|
90
100
|
- VERSION
|
91
101
|
- examples/a_plus_b.rb
|
102
|
+
- examples/benchmark.rb
|
103
|
+
- examples/benchmark/compiled_not_indexed.rb
|
104
|
+
- examples/benchmark/compiled_sequence_indexed.rb
|
105
|
+
- examples/benchmark/indexed_procedure.rb
|
106
|
+
- examples/benchmark/prolog.rb
|
107
|
+
- examples/benchmark/pure.rb
|
92
108
|
- examples/checkmate.rb
|
93
109
|
- examples/combination.rb
|
94
110
|
- examples/dcg.rb
|
@@ -101,6 +117,7 @@ files:
|
|
101
117
|
- examples/file_search.rb
|
102
118
|
- examples/hanoi.rb
|
103
119
|
- examples/hello.rb
|
120
|
+
- examples/hu/csaladfa.rb
|
104
121
|
- examples/mice.rb
|
105
122
|
- examples/mice2.rb
|
106
123
|
- examples/n_queens.rb
|
@@ -230,7 +247,6 @@ files:
|
|
230
247
|
- spec/inriasuite_spec.rb
|
231
248
|
- spec/integration/custom_classes_spec.rb
|
232
249
|
- spec/integration/dsl_spec.rb
|
233
|
-
- spec/integration/recursion_spec.rb
|
234
250
|
- spec/integration/theory_as_module_spec.rb
|
235
251
|
- spec/integration/theory_as_module_with_include_spec.rb
|
236
252
|
- spec/rspec/rubylog_spec.rb
|
@@ -264,7 +280,6 @@ files:
|
|
264
280
|
- spec/rubylog/variable_spec.rb
|
265
281
|
- spec/spec_helper.rb
|
266
282
|
- vimrc
|
267
|
-
- README.rdoc.orig
|
268
283
|
homepage: https://github.com/cie/rubylog
|
269
284
|
licenses:
|
270
285
|
- MIT
|
@@ -280,7 +295,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
280
295
|
version: '0'
|
281
296
|
segments:
|
282
297
|
- 0
|
283
|
-
hash:
|
298
|
+
hash: 393356129
|
284
299
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
285
300
|
none: false
|
286
301
|
requirements:
|
data/README.rdoc.orig
DELETED
@@ -1,284 +0,0 @@
|
|
1
|
-
= Rubylog - Prolog interpreter for ruby
|
2
|
-
|
3
|
-
Rubylog is a Prolog-like DSL for Ruby. The language is inspired by {Jamis
|
4
|
-
Buck}[http://weblog.jamisbuck.org/2006/10/28/prolog-in-ruby], and the
|
5
|
-
implementation is based on {Yield Prolog}[http://yieldprolog.sourceforge.net/],
|
6
|
-
with lots of sintactic and semantic additions.
|
7
|
-
|
8
|
-
See the {wiki}[https://github.com/cie/rubylog/wiki] for online documentation.
|
9
|
-
|
10
|
-
== Getting started
|
11
|
-
|
12
|
-
First, install the gem
|
13
|
-
|
14
|
-
$ gem install rubylog
|
15
|
-
|
16
|
-
or, if you use +bundler+, add this line to your +Gemfile+:
|
17
|
-
|
18
|
-
gem 'rubylog', '~>2.0pre1'
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
First, you need a Rubylog context. The simplest you can do is to extend Rubylog::Context into the main object.
|
23
|
-
|
24
|
-
require 'rubylog'
|
25
|
-
extend Rubylog::Context
|
26
|
-
|
27
|
-
|
28
|
-
=== Data types
|
29
|
-
|
30
|
-
Rubylog is similar to Prolog, but there are quite a few differences. In Rubylog,
|
31
|
-
you can use any Ruby object as data.
|
32
|
-
|
33
|
-
Rubylog variables are (undefined) constant names:
|
34
|
-
|
35
|
-
A, B, ANYTHING
|
36
|
-
|
37
|
-
A variables whose name starts with +ANY...+ (case-insensitive) is a don't-care
|
38
|
-
variable (like +_+ in Prolog).
|
39
|
-
|
40
|
-
<<<<<<< HEAD
|
41
|
-
Structures are in a different order than they are in prolog:
|
42
|
-
|
43
|
-
functor_for String, :likes
|
44
|
-
'John'.likes('beer')
|
45
|
-
|
46
|
-
which would be <tt>likes('John','beer')</tt> in prolog.
|
47
|
-
|
48
|
-
=======
|
49
|
-
>>>>>>> develop
|
50
|
-
Lists are just Ruby arrays:
|
51
|
-
|
52
|
-
[1, 2, 3]
|
53
|
-
|
54
|
-
They can have splats:
|
55
|
-
|
56
|
-
[1, 2, *T]
|
57
|
-
|
58
|
-
Which would be <tt>[1,2|T]</tt> in Prolog, however, in Rubylog, splats are not limited
|
59
|
-
to the end.
|
60
|
-
|
61
|
-
=== Predicates
|
62
|
-
As in prolog, predicates are the buinding blocks of your program. However, the arguments are in a different order than they are in prolog:
|
63
|
-
|
64
|
-
predicate_for String, ".likes()"
|
65
|
-
'John'.likes!('beer')
|
66
|
-
|
67
|
-
which would be <tt>likes('John','beer').</tt> in prolog. In Rubylog, predicates must be declared. The string indicating the predicate syntax is <tt>".likes()"</tt>. The format is <tt>:asdf .asdf .asdf() .asdf(,) .asdf(,,)</tt> for predicates with 0,1,2,3,4 arguments.
|
68
|
-
|
69
|
-
You can assert a rule with the +if+ method:
|
70
|
-
|
71
|
-
predicate_for String, ".drinks() .has()"
|
72
|
-
X.drinks(Y).if X.has(Y).and X.likes(Y)
|
73
|
-
|
74
|
-
This would be +drinks(X,Y) :- has(X,Y), likes(X,Y)+ in Prolog.
|
75
|
-
|
76
|
-
You can assert facts with <tt>if(:true)</tt>, or, as a shorthand you can use the bang
|
77
|
-
syntax:
|
78
|
-
|
79
|
-
'John'.likes! 'milk'
|
80
|
-
|
81
|
-
Bang assertions return their first argument (which is <tt>'John'</tt> in this case), so they can be chained:
|
82
|
-
|
83
|
-
'John'.likes!('beer').has!('beer')
|
84
|
-
|
85
|
-
You can also use +unless+:
|
86
|
-
|
87
|
-
predicate_for String, ".good .bad"
|
88
|
-
A.good.unless A.bad
|
89
|
-
|
90
|
-
Nullary predicates are symbols, similar to Prolog:
|
91
|
-
|
92
|
-
'John'.drinks('beer').if :false.and(:cut!).or(:true)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
=== Built-in predicates
|
97
|
-
|
98
|
-
Some built-in predicates and their Prolog equivalents:
|
99
|
-
|
100
|
-
<<<<<<< HEAD
|
101
|
-
Prolog Rubylog
|
102
|
-
------ -------
|
103
|
-
true/0 :true
|
104
|
-
fail/0 :fail
|
105
|
-
','/2 .and()
|
106
|
-
';'/2 .or()
|
107
|
-
'\+'/1 .false
|
108
|
-
'='/2 .is()
|
109
|
-
'=/='/2 .is_not()
|
110
|
-
member/2 .in()
|
111
|
-
'!'/0 :cut!
|
112
|
-
=======
|
113
|
-
Rubylog Prolog
|
114
|
-
------- ------
|
115
|
-
:true true
|
116
|
-
:fail fail
|
117
|
-
.and() ,
|
118
|
-
.or() ;
|
119
|
-
:false \+
|
120
|
-
.is() =
|
121
|
-
.is_not() =/=
|
122
|
-
.in() member
|
123
|
-
:cut! !
|
124
|
-
>>>>>>> develop
|
125
|
-
|
126
|
-
There are some new ones:
|
127
|
-
|
128
|
-
not_in, all, any, one, none, iff
|
129
|
-
|
130
|
-
You can see reference of these in <tt>lib/rubylog/builtins/logic.rb</tt> and <tt>lib/rubylog/builtins/term.rb</tt>
|
131
|
-
|
132
|
-
|
133
|
-
See the documentation in <tt>lib/rubylog/builtins/</tt>
|
134
|
-
|
135
|
-
=== Unification
|
136
|
-
|
137
|
-
In Rubylog, unification works quite the same in Prolog, with the +is+ functor.
|
138
|
-
|
139
|
-
A.is(B)
|
140
|
-
|
141
|
-
Using arrays, you can benefit the splats:
|
142
|
-
|
143
|
-
[1,2,3,4].is([A,B,*T]) # [1,2,3,4] = [A,B|T] in prolog
|
144
|
-
[1,2,3,4].is([*H,*T]) # append(H, T, [1,2,3,4]) in prolog
|
145
|
-
|
146
|
-
The +in+ predicate unifies the first argument with any member of the collection:
|
147
|
-
|
148
|
-
4.in([1,2,3,4])
|
149
|
-
|
150
|
-
You can use guards:
|
151
|
-
|
152
|
-
A[String].in(["asdf",5,nil]).each { p A } # outputs "asdf"
|
153
|
-
A[/x/].in(["asdf","xyz"]).each { p A } # outputs "xyz"
|
154
|
-
A[thats < 5].in([4,5,6]).each { p A } # outputs 4
|
155
|
-
|
156
|
-
=== Moving between Ruby and Rubylog
|
157
|
-
==== Running a query
|
158
|
-
|
159
|
-
If you want to run a query, you have many different syntaxes:
|
160
|
-
|
161
|
-
prove ('John'.drinks 'beer') # => true
|
162
|
-
true? ('John'.drinks 'beer') # => true
|
163
|
-
('John'.drinks 'beer').true? # => true
|
164
|
-
'John'.drinks? 'beer' # => true
|
165
|
-
|
166
|
-
==== Enumerations
|
167
|
-
|
168
|
-
+Structure+ implements +Enumerable+, and yields the solutions. Within the
|
169
|
-
enumeration block, you can access the values of your variables.
|
170
|
-
|
171
|
-
'John'.drinks! 'beer'
|
172
|
-
('John'.drinks X).each {p X} # outputs 'beer'
|
173
|
-
('John'.drinks X).map{X} # => ['beer']
|
174
|
-
('John'.drinks X).count # => 1
|
175
|
-
|
176
|
-
==== Procs as predicates
|
177
|
-
|
178
|
-
You can invoke Ruby codes in Rubylog rules with a proc:
|
179
|
-
|
180
|
-
'John'.likes(Y).if proc{ Y =~ /ale/ }
|
181
|
-
|
182
|
-
or in most cases you can use just a block:
|
183
|
-
|
184
|
-
'John'.likes(Y).if { Y =~ /ale/ }
|
185
|
-
|
186
|
-
The predicate succeeds if the block returns a true value.
|
187
|
-
|
188
|
-
==== Procs as functions
|
189
|
-
|
190
|
-
+is+ and +in+ can take a proc or block argument, which they execute and take its return value:
|
191
|
-
|
192
|
-
X.good.if X.is { 'BEER'.downcase }
|
193
|
-
X.good.if X.in { get_good_drinks() }
|
194
|
-
|
195
|
-
==== The two modes of Rubylog
|
196
|
-
|
197
|
-
Rubylog has two modes, DSL and native. DSL code is executed only once
|
198
|
-
at compile time, and is used for describing the Rubylog program. Native code is
|
199
|
-
executed runtime. Any block passed to Rubylog structures native code.
|
200
|
-
|
201
|
-
('John'.drinks X).and { X != 'beer'}.each { p X }
|
202
|
-
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ dsl mode
|
203
|
-
^^^^^^^^^^^^ ^^^^^ native mode
|
204
|
-
|
205
|
-
In dsl mode, variables are +Rubylog::Variable+ objects. In native mode,
|
206
|
-
variables are substituted with their respecitve value (or +nil+ if they are not
|
207
|
-
bound).
|
208
|
-
|
209
|
-
All built-in rubylog predicates are clean logical programming predicates witout
|
210
|
-
a side-effect. If you want some side-effects, you always go into native mode.
|
211
|
-
|
212
|
-
=== Rubylog as a test suite
|
213
|
-
|
214
|
-
You can write simple tests using the +check+ method:
|
215
|
-
|
216
|
-
theory do
|
217
|
-
check :true.or :false
|
218
|
-
check A.is(3).and A.in [1,2,3]
|
219
|
-
check { 5+5 == 10 }
|
220
|
-
end
|
221
|
-
|
222
|
-
You sould put this file in <tt>"./logic/something\_logic.rb"</tt>. Then you can run it
|
223
|
-
with
|
224
|
-
|
225
|
-
rubylog logic/something_logic.rb
|
226
|
-
|
227
|
-
Or you can run all files in <tt>logic/**/*\_logic.rb</tt> with
|
228
|
-
|
229
|
-
rubylog
|
230
|
-
|
231
|
-
=== Other built-in libraries
|
232
|
-
|
233
|
-
==== File system
|
234
|
-
|
235
|
-
You can make some queries on the file system:
|
236
|
-
|
237
|
-
require "rubylog/builtins/file_system"
|
238
|
-
|
239
|
-
theory do
|
240
|
-
check "README".filename_in "."
|
241
|
-
|
242
|
-
X.dirname_in(".").each { puts X }
|
243
|
-
end
|
244
|
-
|
245
|
-
|
246
|
-
==== Reflection
|
247
|
-
|
248
|
-
You can make some metaprogramming with Rubylog
|
249
|
-
|
250
|
-
|
251
|
-
require "rubylog/builtins/reflection"
|
252
|
-
|
253
|
-
theory do
|
254
|
-
functor_for String, :likes
|
255
|
-
|
256
|
-
check "John".likes("Jane").structure(:likes, ["John", "Jane"])
|
257
|
-
|
258
|
-
"John".likes(X).if X.likes("John")
|
259
|
-
"Jane".likes!("John")
|
260
|
-
check "John".likes("Jane").follows_from "Jane".likes("John")
|
261
|
-
|
262
|
-
"John".likes!("milk")
|
263
|
-
check "John".likes("milk").fact
|
264
|
-
check "John".likes("beer").fact.false
|
265
|
-
|
266
|
-
end
|
267
|
-
|
268
|
-
== Contributing
|
269
|
-
|
270
|
-
=== To the language
|
271
|
-
|
272
|
-
* Post your own examples to the {wiki}[https://github.com/cie/rubylog/wiki/Examples].
|
273
|
-
* Improve others' examples.
|
274
|
-
* If you have a suggestion for the language, submit an issue.
|
275
|
-
|
276
|
-
=== Reporting bugs or requesting features
|
277
|
-
|
278
|
-
* Create an issue on the {issue tracker}[https://github.com/cie/rubylog/issues].
|
279
|
-
|
280
|
-
== Copyright
|
281
|
-
|
282
|
-
Copyright (c) 2013 Bernát Kalló. See LICENSE.txt for
|
283
|
-
further details.
|
284
|
-
|
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "Recursion", :rubylog=>true do
|
4
|
-
predicate_for Integer, ".factorial()"
|
5
|
-
0.factorial! 1
|
6
|
-
N[thats > 0].factorial(K).if N1.is {N-1} .and N1.factorial(K1).and K.is{ N*K1 }
|
7
|
-
|
8
|
-
check 0.factorial 1
|
9
|
-
check 1.factorial 1
|
10
|
-
check 2.factorial 2
|
11
|
-
check 3.factorial 6
|
12
|
-
check 4.factorial 24
|
13
|
-
check 7.factorial 5040
|
14
|
-
end
|