[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: computer-go: Languages for programming go are?
I'll reply to this since I think the question is interesting, but
remember to take my opinions with a big grain of salt, since Anders'
program can easily stomp my program.:-|
On Fri, Dec 29, 2000 at 03:48:51PM -0800, Anders Kierulf wrote:
> > 2) If one were to imagine a new language, invented solely for
> > the purpose of writing a great computer go system, what would
> > its characteristics likely be? (Or would there be multiple
> > new languages, for different parts of the problem?)
> > ...
> > Bob Myers
> > Intelligent Go Foundation
>
> In a sense, everybody developing a Go program is designing their own
> language for computer Go. The base code of a Go program provides types
> and functions that implement board management (such as blocks,
> liberties, playing and taking back a move), bitboards, pattern
> matching, hash tables, and more, and higher-level code (such as groups
> and tactical search) is based on those functions. Whether that base
> code is provided as a toolkit or as a language is not a crucial issue,
> as long as the desired higher-level Go concepts can be expressed in
> terms of the base code.
The topic sentence is sort of like the "library design is language
design, and vice versa" rule that I think Bjarne Stroustrup attributes
to Bell Labs culture. And I basically agree with the entire paragraph.
> SmartGo is written in C++, and while C++ is far from ideal, it lets
> us efficiently express the concepts we want to express. Our web site
> explains how C++ is used in SmartGo, see
> http://www.smartgo.com/t_use_of_cpp.htm. That web page also shows the
> method ForceApproachMove exactly as written in SmartGo. It would be
> very interesting to see how other Go programs express that algorithm,
> not in pseudo-code, but in real code that will actually work as
> written, and is efficient enough to generate moves during a tactical
> search that examines thousands of positions per second. Concrete
> examples like this is where the rubber hits the road. How would this
> look in Lisp? In GNU Go? In Many Faces of Go?
Low-level C++ code like the example on your web page looks very
similar in Lisp. The biggest difference is that the usual idiom is
often an iteration macro or a mapping function instead of an iterator
class, so e.g.
for (LibertyIterator iter(board, block); iter; ++iter) {
..
}
will often be something like
(do-block-libs (lib-iter board block)
..)
or
(map-on-block-libs (lambda (lib-iter) ..) board block)
instead of the more literal translation
(do ((iter (liberty-iter board block) (next iter)))
((null iter))
..)
There's seldom any great advantage to using Lisp at this level.
Lisp comes into its own in higher level code. Sometimes Lisp bigots
claim that the _Design Patterns_ book is mostly unnecessary in Lisp
because things like that are so much easier to write in Lisp. That's
an exaggeration, but it is true that concepts at that level are often
easier to express in Lisp than in C++.
Memoization/uniquification is a good example of a design theme which
is easy to express in Lisp and (AFAIK) more tedious to express in C++.
In my Go program, I have a single macro which memoizes a constructor
automatically if the class of objects is unique. It's moderately
complicated, a little more complicated than hand-crafting such a
memoization scheme for a single class in C++. But in Lisp I do it once
and can then use it for all classes afterwards without needing to
think about it again. The logic in the macro used in my program is
fairly hairy because of the way it deals with backtracking and
complicated argument lists and minimizing dynamic memory allocation
and a few other issues, and besides that Lisp macros are hard to read
for non-Lisp programmers, so I despite your suggestion that I show
real working code, I don't really want to use it as an example.
Instead, I'll show a textbook implementation of memoization, based on
the memoizing higher-order function from Figure 5.2 of Paul Graham's
_On Lisp_:
;;; Given an unmemoized function, return a memoized version.
(defun memoized (fn)
(let ((cache (make-hash-table :test 'equal)))
(lambda (&rest args)
(multiple-value-bind (val found?) (gethash args cache)
(if found?
val
(setf (gethash args cache)
(apply fn args)))))))
;;; Overwrite the bare function definition of FN-NAME with a
;;; memoized version.
(defun memoize! (fn-name)
(setf (symbol-function fn-name)
(memoized (symbol-function fn-name))))
This memoization utility can be used in a program like this:
;;; Define a bare constructor, which we don't want to call more
;;; than once with any set of arguments, because we'd like there
;;; to be only one "connection between G1 and G2" object in
;;; existence for any given pair of values G1 and G2.
(defun make-connection (g1 g2)
..)
;;; So we overwrite it with the memoized version.
(memoize! 'make-connection)
;;; Define some search function which is expensive to compute,
;;; enough so that we want to avoid calling it more than once
;;; with any given set of arguments.
(defun best-compound-connection (square-1 square-2 player)
..)
;;; So we overwrite it with the memoized version.
(memoize! 'best-compound-connection)
;;; etc.
..
The generic once-for-all-classes memoization logic in my program is
not as efficient as hand-crafted C++ class-specific versions could be.
But it's so much more concise (than any C++ version that I know how to
write) that I find it substantially easier to write and read and
debug. Also, when I'm writing C++ code, if I spent too much time
writing and debugging hand-crafted code, I might find that I don't
have time to implement memoization at all; and memoized Lisp code can
be a lot more efficient than C++ code which doesn't memoize!
So, IMHO it's worth considering using languages which use a higher
level of abstraction than C++, but the benefits may not be visible in
code at low levels of abstraction.
--
William Harold Newman <william.newman@xxxxxxxxxxxxxxxxx>
software consultant
PGP key fingerprint 85 CE 1C BA 79 8D 51 8C B9 25 FB EE E0 C3 E5 7C