Builtin Predicates - wizardofosmium/porolog GitHub Wiki
Porolog Builtin Predicates
- write(*args)
- writenl(*args)
- nl(goal, block)
- is(variable, *args, &is_block)
- var(variable)
- nonvar(variable)
- atom(variable)
- atomic(variable)
- integer(variable)
- eq(x, y)
- is_eq(x, y)
- ruby(*args, &ruby_block)
- noteq(x, y)
- is_noteq(variable, all_values, *exclusions)
- less(x, y)
- gtr(x, y)
- lesseq(x, y)
- gtreq(x, y)
- length(list, length)
- between(variable, lower, upper)
- member(element, list, limit = 100)
- append(front, back, front_back)
- permutation(list1, list2)
- reverse(list1, list2)
write(*args)
Outputs all arguments with two conversions:
- Symbols are replaced by their instantiated value or their name if uninstantiated, and
- Arrays are output using
inspect
Calls $stdout.print
.
writenl(*args)
Functions like write
except a new line is added.
Calls $stdout.puts
.
nl(goal, block)
Outputs a new line.
Calls $stdout.puts
.
is(variable, *args, &is_block)
Instantiates, if possible, the variable to the result of the provided block. The block is passed the values of the arguments.
For example, if you want to instantiate Y
to the value of X
plus one
is(:Y, :X) {|x| x + 1 }
If X
is instantiated, then x
will be its value.
If X
is uninstantiated, then x + 1
will raise an exception. To guard against this, you can either ensure X
is instantiated
by logic or check it is instantiated in Ruby code in the block.
In logic, you could simply use:
nonvar(:X),
is(:Y, :X) {|x| x + 1 }
In Ruby, you can test if it is a variable
is(:Y, :X) {|x| x.type != :variable && x + 1 }
and when the result of the block is nil
or false
, the is
goal will fail, triggering backtracking.
If Y
is uninstantiated, then it is instantiated with the result of the block, unless it is nil
or false
, and the goal succeeds.
If Y
is already instantiated, then the is
goal only succeeds if Y
can be unified with the result of the block.
var(variable)
Succeeds if the argument is an uninstantiated variable; otherwise it fails.
nonvar(variable)
Succeeds if the argument is not an uninstantiated variable; otherwise it fails.
atom(variable)
Succeeds if the argument is a String; otherwise it fails.
atomic(variable)
Succeeds if the argument is not an uninstantiated variable and not an Array; otherwise it fails.
integer(variable)
Succeeds if the argument is an Integer; otherwise it fails.
eq(x, y)
If the arguments are nonvars, their equality is checked.
eq([3,2,1,4], [1,2,3,4]) # fails
eq([1,2,3,4], [1,2,3,4]) # succeeds
Uninstantiated variable arguments succeed if they are bound together.
eq(:X, :X) # succeeds
eq(:X, :Y) # depends on whether X and Y are bound directly or indirectly
Uninstantiated variables are not instantiated.
eq([1,2,3,4], :L) # fail
eq(:L, [1,2,3,4]) # fail
is_eq(x, y)
Implements a synthesis of is
and eq
, instantiating the left hand side variable if possible.
is_eq([1,2,3,4], :L) # fail unless L is instantiated to [1,2,3,4]
is_eq(:L, [1,2,3,4]) # succeed and instantiate L to [1,2,3,4] if L is uninstantiated
ruby(*args, &ruby_block)
Allows Ruby code to be inserted into rules. The ruby
predicate succeeds by default.
To make a ruby
predicate fail, return the Symbol :fail
.
ruby(:L, :name) {|l, name|
CSV.open(name, 'w') do |csv|
l.each do |element|
csv << [*element]
end
end
}
ruby(:X) {|x|
$stdout.puts "x is now #{x.inspect}, forcing backtracking ..."
:fail
}
noteq(x, y)
If the arguments are nonvars, their equality is checked.
noteq([3,2,1,4], [1,2,3,4]) # succeeds
noteq([1,2,3,4], [1,2,3,4]) # fails
Uninstantiated variable arguments fail if they are bound together.
noteq(:X, :X) # fails
noteq(:X, :Y) # depends on whether X and Y are bound directly or indirectly
Uninstantiated variables are not instantiated but do succeed.
noteq([1,2,3,4], :L) # succeed
noteq(:L, [1,2,3,4]) # succeed
is_noteq(variable, all_values, *exclusions)
is_noteq
implements a simple constraint mechanism. Its purpose is to provide an efficiency
when dealing with simple constraints where:
- the
variable
must be one of the values ofall_values
, - the
variable
must not be one of the values ofexclusions
, and - additionally, all exclusions must be unique values.
Then, it functions as member(variable, all_values_less_exclusions)
.
is_noteq(:X, [1,2,3,4,5], :Y, :Z)
The above succeeds if Y and Z are different and then successively instantiates X to each of the remaining values except the values of Y and Z. If X is already instantiated, compares membership instead of instantiating.
less(x, y)
The first argument must be less than the second argument.
less(4, 7) # succeed
less('b', 'h') # succeed
less(1.003, 1.01) # succeed
less(7, 4) # fail
less('h', 'b') # fail
less(1.01, 1.003) # fail
less(:X, :X) # fail
gtr(x, y)
The first argument must be greater than the second argument.
gtr(7, 4) # succeed
gtr('h', 'b') # succeed
gtr(1.01, 1.003) # succeed
gtr(4, 7) # fail
gtr('b', 'h') # fail
gtr(1.003, 1.01) # fail
gtr(:X, :X) # fail
lesseq(x, y)
The first argument must be less than or equal the second argument. Uninstantiated variables fail unless they are bound to each other.
lesseq(:X, :X) # succeed
gtreq(x, y)
The first argument must be greater than or equal the second argument. Uninstantiated variables fail unless they are bound to each other.
gtreq(:X, :X) # succeed
length(list, length)
length()
succeeds if the length of the list is equal to the value of the second argument.
If length
is uninstantiated, then it is instantiated to the length of the list.
If list
is uninstantiated, then it is instantiated to a list of length length
of anonymous variables.
between(variable, lower, upper)
Succeeds if variable
is in the inclusive range of lower
to upper
.
If variable
is uninstantiated, it is instantiated to each value of the range on backtracking.
If lower
or upper
are uninstantiated, between
instantiates them to create the narrowest range.
between(:I, 1, 10)
between(4, 1, :upper) # upper --> 4
between(4, :lower, 10) # lower --> 4
between(-7, 1, :upper) # fail
between(24, :lower, 10) # fail
between(24, :lower, :upper) # lower --> 24, upper --> 24
member(element, list, limit = 100)
Succeeds when element
is a member of list
.
If variables are present, member
tries to instantiate the variables to produce a successful result;
otherwise it fails.
If element
is uninstantiated, it is successively instantiated to each member of the list.
assert_solutions member(3, [1,2,:C,4]), [{ C: 3 }]
assert_solutions member([:A,3], [1,2],[2,3],[3,4](/wizardofosmium/porolog/wiki/1,2],[2,3],[3,4)), [{ A: 2 }]
assert_solutions member(:M, [1,2],[2,3],[3,4](/wizardofosmium/porolog/wiki/1,2],[2,3],[3,4)), [
If list
is uninstantiated, then the third parameter sets the limit of
the number of solutions where list is instantiated to lists where element
is in each subsequent position in each list. For example,
member(:X, :L, 4)
produces
L --> [X, ...]
L --> [_, X, ...]
L --> [_, _, X, ...]
L --> [_, _, _, X, ...]
append(front, back, front_back)
Succeeds when the front
list appended with the back
list equals the front_back
list.
Any variables are instantiated to satisfy this condition.
It fails when the lists are not equal.
If there are multiple ways to satisfy the condition, each solution is successively instantiated on backtracking. For example,
append(:M, :L, [1,2,3,4,5])
produces
M --> [], L --> [1, 2, 3, 4, 5]
M --> [1], L --> [2, 3, 4, 5]
M --> [1, 2], L --> [3, 4, 5]
M --> [1, 2, 3], L --> [4, 5]
M --> [1, 2, 3, 4], L --> [5]
M --> [1, 2, 3, 4, 5], L --> []
append()
also binds front
, back
, and front_back
so that if they are instantiated later, they will still
obey the append condition.
permutation(list1, list2)
Succeeds when the list arguments are a permutation of each other. Embedded and direct variables will be instantiated to satisfy the condition. Where there are multiple possible instantiations, each will be successively instantiated on backtracking. For example,
permutation([:A, 2, 1, 4], [1, 2, :C, 4])
produces
A --> 1, C --> 1
A --> 2, C --> 2
A --> nil, C --> nil
A --> 4, C --> 4
Note: nil
indicates an uninstantiated variable.
reverse(list1, list2)
Succeeds when the list arguments are the reverse of each other. Embedded and direct variables will be instantiated to satisfy the condition.