ambit 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +23 -0
- data/.gemtest +0 -0
- data/History.txt +3 -0
- data/LICENSE.txt +28 -0
- data/Manifest.txt +11 -0
- data/README.rdoc +427 -0
- data/README.txt +427 -0
- data/Rakefile +12 -0
- data/examples/example.rb +61 -0
- data/examples/queens.rb +127 -0
- data/lib/ambit.rb +101 -0
- data/test/test_ambit.rb +68 -0
- metadata +103 -0
data/README.txt
ADDED
@@ -0,0 +1,427 @@
|
|
1
|
+
= ambit
|
2
|
+
|
3
|
+
https://github.com/jimwise/ruby/tree/master/ambit
|
4
|
+
|
5
|
+
Author:: Jim Wise (mailto:jwise@draga.com)
|
6
|
+
Copyright:: Copyright (c) 2011 Jim Wise
|
7
|
+
License:: 2-clause BSD-Style (see LICENSE.txt)
|
8
|
+
|
9
|
+
== DESCRIPTION:
|
10
|
+
|
11
|
+
This is an all-ruby implementation of choose/fail nondeterministic
|
12
|
+
programming with branch cut, as described in Chapter 22 of Paul Graham's
|
13
|
+
<em>On Lisp</em>[1], Chapter, or Section 4.3 of <em>SICP</em>[2].
|
14
|
+
|
15
|
+
Due to Ruby containing a true call/cc, this is a much straighter port of
|
16
|
+
Paul Graham's scheme version of this code than his Common Lisp or my C
|
17
|
+
versions are. :-)
|
18
|
+
|
19
|
+
== REQUIREMENTS:
|
20
|
+
|
21
|
+
<b>This code will not work in JRuby or MacRuby (no callcc). It should work
|
22
|
+
in Ruby 1.9 with minor changes (callcc has moved to the 'continuation'
|
23
|
+
stdlib).</b>
|
24
|
+
|
25
|
+
== INSTALL:
|
26
|
+
|
27
|
+
To install:
|
28
|
+
|
29
|
+
$ gem install ambit
|
30
|
+
|
31
|
+
== DEVELOPERS:
|
32
|
+
|
33
|
+
After checking out the source, run:
|
34
|
+
|
35
|
+
$ rake newb
|
36
|
+
|
37
|
+
This task will install any missing dependencies, run the tests/specs,
|
38
|
+
and generate the RDoc.
|
39
|
+
|
40
|
+
== SYNOPSIS:
|
41
|
+
|
42
|
+
=== What is Nondeterministic Programming?
|
43
|
+
|
44
|
+
Nondeterministic programming is a novel approach to problems where a
|
45
|
+
program must find a working solution out of many possible choices. It
|
46
|
+
greatly simplifies problems such graph searching, testing combinations of
|
47
|
+
values, and so on, where there are many possible values to consider, often
|
48
|
+
in some sort of hierarchical order, but the right combination is not known
|
49
|
+
in advance.
|
50
|
+
|
51
|
+
In such a situation, it can be useful to develop a program by pretending
|
52
|
+
our programming language includes knowledge of the future -- and is thus
|
53
|
+
able to _choose_ the right answer off the bat, and simply programming as
|
54
|
+
if this is the case.
|
55
|
+
|
56
|
+
A language with support for nondeterministic programming (such as Ruby
|
57
|
+
with this gem) helps us keep up this pretense by saving the state of
|
58
|
+
computation (with some limits) whenever we make an important choice. If
|
59
|
+
we later determine that we did _not_, in fact, make the correct choice
|
60
|
+
(lacking true language support for knowing the future), we can _fail_ the
|
61
|
+
current computation, which <em>causes computation to rewind to the last
|
62
|
+
choice made, and continue as if a different choice had been made</em>.
|
63
|
+
|
64
|
+
When all possible choices have been tried, the next time computation
|
65
|
+
_fails_, computation will be rewound to the previous choice point, and
|
66
|
+
will continue with the next possible choice from there.
|
67
|
+
|
68
|
+
Imagine, for instance, that we wish to test a combination lock with a
|
69
|
+
three-number combination, with each number between 1 and 10, inclusive:
|
70
|
+
Instead of writing code ourself to try every possible combination, we
|
71
|
+
simply proceed as if each choice was the correct one, failing if the lock
|
72
|
+
fails to open. In short:
|
73
|
+
|
74
|
+
first = Ambit.choose(1..10)
|
75
|
+
second = Ambit.choose(1..10)
|
76
|
+
third = Ambit.choose(1..10)
|
77
|
+
|
78
|
+
Ambit.fail! unless open_lock(first, second, third)
|
79
|
+
|
80
|
+
# when we get here, lock is open!
|
81
|
+
|
82
|
+
As our language does not actually implement knowledge of the future, this
|
83
|
+
will still try as many combinations as are needed to find the right one --
|
84
|
+
but we can program as if it has chosen the right one on the first try!
|
85
|
+
|
86
|
+
=== How to Use This Gem
|
87
|
+
|
88
|
+
To get started, include this gem using
|
89
|
+
|
90
|
+
require 'rubygems'
|
91
|
+
require 'ambit
|
92
|
+
|
93
|
+
This gem provides the Ambit module. This module provides several methods
|
94
|
+
which implement nondeterministic programming.
|
95
|
+
|
96
|
+
==== Choosing and Failing
|
97
|
+
|
98
|
+
The central method of Ambit is Ambit::choose.
|
99
|
+
|
100
|
+
Ambit::choose takes any enumerable (actually, any object which responds to
|
101
|
+
#each) as an argument, and begins a nondeterministic generate-and-test
|
102
|
+
process with the members of this object.
|
103
|
+
|
104
|
+
Ambit::choose immediately returns the first member of the enumerable, or
|
105
|
+
calls Ambit::fail! if the enumerable is empty:
|
106
|
+
|
107
|
+
a = Ambit::choose([1, 2, 3])
|
108
|
+
puts a
|
109
|
+
prints
|
110
|
+
1
|
111
|
+
|
112
|
+
If, later, Ambit::fail! is called, <em>computation is rewound until the
|
113
|
+
point when Ambit::choose was last called</em>, and the next member of the
|
114
|
+
enumerable is returned <em>from the same call to Ambit::choose</em>:
|
115
|
+
|
116
|
+
a = Ambit::choose([1, 2, 3])
|
117
|
+
Ambit::fail! unless a.even?
|
118
|
+
puts a
|
119
|
+
prints
|
120
|
+
2
|
121
|
+
(and only "2")
|
122
|
+
|
123
|
+
As an alternative, Ambit::assert can be used to fail if a condition holds;
|
124
|
+
Ambit::assert will rewind to the previous invocation of Ambit::choose if
|
125
|
+
and only if it's (single) argument is false:
|
126
|
+
|
127
|
+
a = Ambit::choose([1, 2, 3])
|
128
|
+
Ambit::assert a.even?
|
129
|
+
puts a
|
130
|
+
prints
|
131
|
+
2
|
132
|
+
(and only "2")
|
133
|
+
|
134
|
+
Note that this call to Ambit::fail! (or Ambit::assert) can occur any amount
|
135
|
+
of time later, and works <em>even if the function which called choose has
|
136
|
+
since exited</em>. Execution is still rewound as needed to allow the next
|
137
|
+
value to be returned from the same call to Ambit::choose.
|
138
|
+
|
139
|
+
Calls to Ambit::choose can be nested to arbitrary depth -- each call to
|
140
|
+
Ambit::fail! will rewind to the <em>most recent</em> call to Ambit::choose.
|
141
|
+
If that set of choices has already returned every member of its enumerable,
|
142
|
+
execution is instead rewound to the previous invocation of Ambit::choose,
|
143
|
+
and execution continues with the next choice from that invocation's
|
144
|
+
enumerable:
|
145
|
+
|
146
|
+
a = Ambit::choose([1, 3, 5, 7, 9, 11, 13, 15])
|
147
|
+
b = Ambit::choose([0, 5, 10, 15])
|
148
|
+
Ambit::assert a == b
|
149
|
+
puts a
|
150
|
+
prints
|
151
|
+
5
|
152
|
+
(and only "5")
|
153
|
+
|
154
|
+
If all choices from all past calls to Ambit::choose have been exhausted (or
|
155
|
+
if Ambit::fail! is called before any call to Ambit::choose), an exception of
|
156
|
+
type Ambit::ChoicesExhausted is raised instead.
|
157
|
+
|
158
|
+
==== Side Effects
|
159
|
+
|
160
|
+
We've talked a lot above about "rewinding" computation to a previous choice
|
161
|
+
point. Not all computations can be rewound, however -- if the computation
|
162
|
+
we have performed since the choice point we are rewinding to has had side
|
163
|
+
effects (other than the choices made), those side effects will not
|
164
|
+
themselves be rewound. While some side effects (setting of variables) could
|
165
|
+
theoretically be tracked and undone, this would require very careful
|
166
|
+
semantics. And other side effects could not be undone by any level of
|
167
|
+
complexity added to our language -- if we have printed output to the user,
|
168
|
+
for instance, no amount of rewinding will make the user forget what he has
|
169
|
+
seen -- while we simulate the ability to see the future and to change the
|
170
|
+
past, we can, in fact, do neither.
|
171
|
+
|
172
|
+
This can sometimes cause confusion. This code, for instance:
|
173
|
+
|
174
|
+
a = Ambit::choose([1, 2, 3])
|
175
|
+
puts a
|
176
|
+
Ambit::fail! unless a.even?
|
177
|
+
|
178
|
+
prints
|
179
|
+
|
180
|
+
1
|
181
|
+
2
|
182
|
+
|
183
|
+
instead of only "2" -- the printing has already been done by the time we
|
184
|
+
call Ambit::fail!.
|
185
|
+
|
186
|
+
Such side effects can also be useful, however. This code:
|
187
|
+
|
188
|
+
i = 0
|
189
|
+
first = Ambit.choose(1..10)
|
190
|
+
second = Ambit.choose(1..10)
|
191
|
+
third = Ambit.choose(1..10)
|
192
|
+
i += 1
|
193
|
+
Ambit.fail! unless open_lock(first, second, third)
|
194
|
+
puts i
|
195
|
+
|
196
|
+
prints out the number of combinations which were tried in total (since +i+
|
197
|
+
remains incremented even when we rewind computation).
|
198
|
+
|
199
|
+
==== More Than One Answer
|
200
|
+
|
201
|
+
Often, more than one combination of choices is interesting to consider -- it
|
202
|
+
may be useful, for instance, to see *all* combinations which do not fail,
|
203
|
+
instead of only the first.
|
204
|
+
|
205
|
+
Since Ambit::fail! will always rewind to the previous choice point, getting
|
206
|
+
more possible combinations is as easy as calling Ambit::fail! in order to
|
207
|
+
try the next combination, even though we have not, strictly, failed -- when
|
208
|
+
no more successful combinations are available, this call to Ambit::fail!
|
209
|
+
will instead raise an exception of type Ambit::ChoicesExhausted
|
210
|
+
|
211
|
+
begin
|
212
|
+
a = Ambit::choose([1, 3, 5, 7, 9, 11, 13, 15])
|
213
|
+
b = Ambit::choose([0, 5, 10, 15])
|
214
|
+
Ambit::assert a == b
|
215
|
+
puts a
|
216
|
+
Ambit::fail!
|
217
|
+
rescue Ambit::ChoicesExhausted
|
218
|
+
puts "Done."
|
219
|
+
end
|
220
|
+
prints
|
221
|
+
5
|
222
|
+
15
|
223
|
+
Done.
|
224
|
+
|
225
|
+
Note that this code, too depends on a side effect -- +a+ is output each time
|
226
|
+
we get a match, even though we then call Ambit::fail! to rewind computation
|
227
|
+
and try the next combination.
|
228
|
+
|
229
|
+
==== Cleaning up
|
230
|
+
|
231
|
+
Ambit::clear! can be called at any time to eliminate all outstanding choices
|
232
|
+
on the default Generator, ending nondeterminism (and allowing any
|
233
|
+
outstanding alternate paths of execution to be garbage collected). This is
|
234
|
+
most useful when a given computation is finished, so that future invocations
|
235
|
+
of Ambit::fail! will not restart the now-finished computation with another
|
236
|
+
choice.
|
237
|
+
|
238
|
+
==== Marking and Cutting
|
239
|
+
|
240
|
+
While Ambit::clear! can be used to abandon an entire set of nondeterministic
|
241
|
+
computations, sometimes it is useful to abandon only one branch of a
|
242
|
+
computation, while still keeping the ability to rewind to the choice which
|
243
|
+
first took us down that branch.
|
244
|
+
|
245
|
+
Suppose, for instance, that we are trying to guess a word with five letters:
|
246
|
+
|
247
|
+
a = Ambit::choose('a'..'z')
|
248
|
+
b = Ambit::choose('a'..'z')
|
249
|
+
c = Ambit::choose('a'..'z')
|
250
|
+
d = Ambit::choose('a'..'z')
|
251
|
+
Ambit::assert good_word(a, b, c, d)
|
252
|
+
print a, b, c, d
|
253
|
+
|
254
|
+
This works. But what if we were able to determine, once all four letters
|
255
|
+
were chosen, whether the first letter was correct? How would we proceed?
|
256
|
+
|
257
|
+
If we failed because the first letter was incorrect, we would continue trying
|
258
|
+
every possible value for the second, third and fourth letters -- even though none of
|
259
|
+
them could be correct. We need a way to rewind to an earlier choice point.
|
260
|
+
|
261
|
+
To work around this, Ambit provides a method, Ambit::cut! which "locks in" a
|
262
|
+
set of past choices, preventing them from being revisited later:
|
263
|
+
|
264
|
+
a = Ambit::choose('a'..'z')
|
265
|
+
Ambit::mark
|
266
|
+
b = Ambit::choose('a'..'z')
|
267
|
+
c = Ambit::choose('a'..'z')
|
268
|
+
d = Ambit::choose('a'..'z')
|
269
|
+
if !good_first_letter(a, b, c, d)
|
270
|
+
Ambit::cut!
|
271
|
+
Ambit::fail!
|
272
|
+
end
|
273
|
+
Ambit::assert good_word(a, b, c, d)
|
274
|
+
print a, b, c, d
|
275
|
+
|
276
|
+
When Ambit::cut! is called in the code above, all choices back to the
|
277
|
+
<em>most recent</em> call of Ambit::mark are wiped out -- the next call to
|
278
|
+
Ambit::fail! will rewind to the most recent Ambit::choose invocation
|
279
|
+
<em>before</em> the most recent call to Ambit::mark.
|
280
|
+
|
281
|
+
Ambit::cut! can also be used without Ambit::fail! to "commit" to all choices
|
282
|
+
since the last call to Ambit::mark -- in this case, we are saying that we
|
283
|
+
know these choices are good, so if we (later) fail, we want to rewind out of
|
284
|
+
the whole current branch of computation.
|
285
|
+
|
286
|
+
==== Private Generators
|
287
|
+
|
288
|
+
In addition to using methods of the Ambit module directly, another option is
|
289
|
+
to allocate a Ambit::Generator object explicitly. All methods of the Ambit
|
290
|
+
module are also available as methods of Ambit::Generator (and in fact, the
|
291
|
+
module allocates a default Generator object to handle all calls made at the
|
292
|
+
module level).
|
293
|
+
|
294
|
+
Ambit::Generator::new can be used to allocate a new Generator:
|
295
|
+
|
296
|
+
nd = Ambit::Generator::new
|
297
|
+
nd.choose('a' .. 'e')
|
298
|
+
|
299
|
+
each object allocated in this fashion has its own set of choices, and
|
300
|
+
failing one will not directly affect others. Nesting choices from different
|
301
|
+
Generators is a good way to make code confusing, however, and should be
|
302
|
+
avoided -- this capability is mainly provided to allow multi-threaded
|
303
|
+
programs to safely use Ambit from more than one thread (see below).
|
304
|
+
|
305
|
+
Ambit::Generator#clear! is provided for the same reason as Ambit::clear!,
|
306
|
+
but it is often clearer to use a new Ambit::Generator object for each
|
307
|
+
unrelated set of nondeterministic computations.
|
308
|
+
|
309
|
+
==== Compatibility
|
310
|
+
|
311
|
+
For historical reasons, Ambit::amb and Ambit::Generator#amb are provided as
|
312
|
+
aliases for Ambit::choose and Ambit::Generator#choose. Likewise, for
|
313
|
+
historical reasons, calling Ambit::amb (and Ambit::Generator#amb) with
|
314
|
+
no arguments is equivalent to calling Ambit::fail! (or
|
315
|
+
Ambit::Generator#fail!).
|
316
|
+
|
317
|
+
For the same reason, Ambit::require and Ambit::Generator#require are
|
318
|
+
provided as aliases for Ambit::assert and Ambit::Generator#assert.
|
319
|
+
|
320
|
+
These aliases allow for a more direct translation of programs written with
|
321
|
+
the *amb* operator discussed in *SICP* and elsewhere.
|
322
|
+
|
323
|
+
==== Interaction with Threading
|
324
|
+
|
325
|
+
Given the strong modifications to flow of control which occur when a path of
|
326
|
+
computation is _failed_, care must be taken when using nondeterministic
|
327
|
+
programming in a multi-threaded program. The two main ways to do this are:
|
328
|
+
|
329
|
+
* perform all nondeterministic programming from a single thread of execution
|
330
|
+
|
331
|
+
* give each thread which will be using nondeterministic programming its own Ambit::Generator object. This can be done easily using thread local variables:
|
332
|
+
|
333
|
+
def nd_begin
|
334
|
+
Thread.current[:AMB] = Ambit::Generator.new
|
335
|
+
end
|
336
|
+
|
337
|
+
def nd_choose choices
|
338
|
+
Thread.current[:AMB].choose choices
|
339
|
+
end
|
340
|
+
|
341
|
+
def nd_fail!
|
342
|
+
Thread.current[:AMB].fail!
|
343
|
+
end
|
344
|
+
|
345
|
+
def nd_clear!
|
346
|
+
Thread.current[:AMB].clear!
|
347
|
+
end
|
348
|
+
|
349
|
+
=== Longer example
|
350
|
+
|
351
|
+
This solution to the N queens problem is inspired by the prolog version in
|
352
|
+
<em>The Art of Prolog</em> by Leon Sterling and Ehud Shapiro[3], but is less
|
353
|
+
elegant, as this is not prolog (and I am not Sterling or Shapiro).
|
354
|
+
|
355
|
+
# we want to place N queens on an NxN chess board. Since we know no two queens
|
356
|
+
# can be in the same row, an array of N integers between 0 and N-1 will do to
|
357
|
+
# represent the placement. Since we know no two queens can be in the same column,
|
358
|
+
# each number from 1 .. N will appear once in this array; this means the solution
|
359
|
+
# is a permutation of 1 .. N
|
360
|
+
|
361
|
+
# Here is the complete board generator. Next is the test if a position is safe.
|
362
|
+
|
363
|
+
def queens n, board = []
|
364
|
+
if board.size == n
|
365
|
+
board
|
366
|
+
else
|
367
|
+
c = Ambit.choose(1..n)
|
368
|
+
Ambit.fail! unless safe board, c
|
369
|
+
queens n, board + [c]
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# board is the first M columns of an NxN board, and is valid so far.
|
374
|
+
# piece is a proposed piece for the M+1th row of the board.
|
375
|
+
# returns true if piece is a valid placement, false otherwise
|
376
|
+
|
377
|
+
def safe board, piece
|
378
|
+
board.each_with_index do |c, r|
|
379
|
+
return false if c == piece # same column
|
380
|
+
# they're on the same diagonal if the distance in columns == the distance in rows
|
381
|
+
rdist = board.size - r
|
382
|
+
cdist = (piece - c).abs
|
383
|
+
return false if rdist == cdist
|
384
|
+
end
|
385
|
+
true
|
386
|
+
end
|
387
|
+
|
388
|
+
The file examples/queens.rb, installed with this gem, contains a version of
|
389
|
+
this with display code, and a command-line driver to print all solutions for
|
390
|
+
a given N.
|
391
|
+
|
392
|
+
=== References
|
393
|
+
|
394
|
+
[1] Graham, Paul, <em>On Lisp</em>, Prentice Hall, 1993. Available online at http://www.paulgraham.com/onlisp.html
|
395
|
+
|
396
|
+
[2] Abelson, Harold and Gerald Jay Sussman, <em>Structure and Interpretation of Computer Programs, 2nd Edition</em>, MIT Press, 1996. Available online at http://mitpress.mit.edu/sicp/
|
397
|
+
|
398
|
+
[3] Sterling, Leon and Ehud Shapiro, <em>The Art of Prolog</em>, MIT Press, 1994
|
399
|
+
|
400
|
+
== LICENSE:
|
401
|
+
|
402
|
+
(The BSD 2-clause License)
|
403
|
+
|
404
|
+
Copyright (c) 2011 Jim Wise
|
405
|
+
All rights reserved.
|
406
|
+
|
407
|
+
Redistribution and use in source and binary forms, with or without
|
408
|
+
modification, are permitted provided that the following conditions
|
409
|
+
are met:
|
410
|
+
|
411
|
+
1. Redistributions of source code must retain the above copyright
|
412
|
+
notice, this list of conditions and the following disclaimer.
|
413
|
+
2. Redistributions in binary form must reproduce the above copyright
|
414
|
+
notice, this list of conditions and the following disclaimer in the
|
415
|
+
documentation and/or other materials provided with the distribution.
|
416
|
+
|
417
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
418
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
419
|
+
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
420
|
+
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
|
421
|
+
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
422
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
423
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
424
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
425
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
426
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
427
|
+
POSSIBILITY OF SUCH DAMAGE.
|
data/Rakefile
ADDED
data/examples/example.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'ambit'
|
5
|
+
|
6
|
+
# Choose 2 example (see _On Lisp_, sec, 22.1 (pp. 286-289)
|
7
|
+
def choose2
|
8
|
+
x = Ambit::choose([1, 2])
|
9
|
+
Ambit::require x.even?
|
10
|
+
x
|
11
|
+
end
|
12
|
+
|
13
|
+
puts "chose #{choose2}"
|
14
|
+
|
15
|
+
Ambit::clear!
|
16
|
+
|
17
|
+
# Parlor Trick example (see _On Lisp_, sec, 22.2 (pp. 290-292)
|
18
|
+
# note that this tests that we have real call/cc, not just downward-facing
|
19
|
+
def two_numbers
|
20
|
+
return Ambit::choose(0..5), Ambit::choose(0..5)
|
21
|
+
end
|
22
|
+
|
23
|
+
def parlor_trick sum
|
24
|
+
a, b = two_numbers
|
25
|
+
Ambit::fail! unless a + b == sum
|
26
|
+
return a, b
|
27
|
+
end
|
28
|
+
|
29
|
+
n = 7
|
30
|
+
x, y = parlor_trick n
|
31
|
+
puts "#{n} is the sum of #{x} and #{y}"
|
32
|
+
|
33
|
+
Ambit::clear!
|
34
|
+
|
35
|
+
# Chocoblob Coin Search example (with cuts) (see _On Lisp_, sec, 22.5 (pp. 298-302)
|
36
|
+
def coin? x
|
37
|
+
[["LA", 1, 2], ["NY", 1, 1], ["Boston", 2, 2]].member? x
|
38
|
+
end
|
39
|
+
|
40
|
+
# (define (coin? x)
|
41
|
+
# (member x '((la 1 2) (ny 1 1) (bos 2 2))))
|
42
|
+
|
43
|
+
# We don't need to create a private generator here, but here's how we would
|
44
|
+
$nd = Ambit::Generator.new
|
45
|
+
def find_boxes
|
46
|
+
city = $nd.choose(["LA", "NY", "Boston"])
|
47
|
+
$nd.mark
|
48
|
+
puts ""
|
49
|
+
store = $nd.choose([1, 2])
|
50
|
+
box = $nd.choose([1, 2])
|
51
|
+
print "#{city} #{store} #{box} "
|
52
|
+
if coin? [city, store, box]
|
53
|
+
$nd.cut!
|
54
|
+
puts "C"
|
55
|
+
end
|
56
|
+
$nd.fail!
|
57
|
+
rescue Ambit::ChoicesExhausted
|
58
|
+
puts "Done."
|
59
|
+
end
|
60
|
+
|
61
|
+
find_boxes
|