rubylog 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|