我正在尝试解决LISP上的问题,我很多天都遇到了这个问题。
"编写一个名为wheres-waldo的函数,它将一个lisp对象(即由conses构建的数据结构)作为参数,并返回一个从该对象中提取符号waldo的Lisp表达式,如果它是本"
例如,
例如:(wheres-waldo'(emerson ralph waldo)) =
OUTPUT: (FIRST (REST (REST '(EMERSON RALPH WALDO))))
OUTPUT: (FIRST (REST (FIRST (REST
'(MENTOR (RALPH WALDO EMERSON)
(HENRY DAVID THOREAU))))))
我写了一些递归,例如,
(defun wheres-waldo(lispOBJ)
(cond ((null lispOBJ) nil)
(equalp (first lispOBJ) waldo)
( t (***stuck here how to write recursion for this***))
)
我从http://ai.eecs.umich.edu/people/wellman/courses/eecs492/f94/MP1.html wheres-waldo找到了这个问题。 任何帮助,将不胜感激。谢谢。
答案 0 :(得分:3)
您需要遍历列表,如果元素是列表,则递归到子列表中,就像实现深度搜索一样。唯一的区别是,为了产生所需的输出,你需要继续使用s-expression来回溯你用来实现的功能。
这是一种可能的实现方式。请注意,我使用了更传统的car
和cdr
而不是first
和rest
- 它们是等效的。
(defun whereis (who obj &optional (sexp (list 'quote obj)))
(cond
; we found the object - return the s-expr
((eq obj who) sexp)
; try car and the cdr
((and obj (listp obj))
(or (whereis who (car obj) (list 'car sexp))
(whereis who (cdr obj) (list 'cdr sexp))))))
然后:
? (whereis 'waldo '(emerson ralph waldo))
(CAR (CDR (CDR '(EMERSON RALPH WALDO))))
? (whereis 'waldo '(mentor (ralph waldo emerson) (henry david thoreau)))
(CAR (CDR (CAR (CDR '(MENTOR (RALPH WALDO EMERSON) (HENRY DAVID THOREAU))))))
? (whereis 'thoreau '(mentor (ralph waldo emerson) (henry david thoreau)))
(CAR (CDR (CDR (CAR (CDR (CDR '(MENTOR (RALPH WALDO EMERSON) (HENRY DAVID THOREAU))))))))
? (whereis 'scotty '(beam me up . scotty))
(CDR (CDR (CDR '(BEAM ME UP . SCOTTY))))
? (whereis 'waldo '(emerson ralph))
NIL
如果您的元素可以出现多次,您还可以构建结果列表:
? (whereis 'c '(a b c d c b a))
((CAR (CDR (CDR '(A B C D C B A)))) (CAR (CDR (CDR (CDR (CDR '(A B C D C B A)))))))
使用此代码:
(defun whereis (who obj)
(let ((res nil)) ; the final result
(labels
; sub-function: walks the whole list recursively
((sub (obj sexp)
; found it - add to result list
(when (eq obj who) (setf res (cons sexp res)))
; try car and cdr
(when (and obj (listp obj))
(sub (cdr obj) (list 'cdr sexp))
(sub (car obj) (list 'car sexp)))))
; call sub-function
(sub obj (list 'quote obj)))
res))
答案 1 :(得分:3)
你的方法的主要问题是,如果第一个元素等于waldo,你认为如何产生答案? waldo可能存在许多可能的路径,因此我们需要一种方法在迭代中指示我们所处的路径,如果我们处于死路,我们需要回溯。
(defun wheres-waldo (o)
(labels ; labels is to make local functions
((aux (cur acc) ; define loacl function aux with args cur and acc
(or ; or stops at the first non NIL value
(and (eq cur 'waldo) acc) ; if (eq cur 'waldo) we return acc
(and (consp cur) ; (else) if object is a cons
(or ; then one of the followin
(aux (car cur) (list 'first acc)) ; answer might be in the car
(aux (cdr cur) (list 'rest acc))))))) ; or the cdr of the cons
(aux o (list 'quote o)))) ; call aux with original object and the same object quoted. (list 'quote x) ==> 'x (as data)
如您所见,主要工作由aux
完成,该waldo
有一个对象和一个累积器,用于表示路径和引号数据。如果找到car
,那么结果就是累加器。
如果waldo存在于多个位置,它总是首先and
,所以不一定是最短的答案,而是它找到的第一个答案。
我在这里使用or
/ if
。这些类似于(and (eq cur 'waldo) acc)
,除了它是返回的表达式的值。如果acc
为cur
,waldo
将确保我们返回and
,因为or
评估为最后一个真值。如果有一个NIL值,它将成为表单的结果。对于NIL
,如果所有表达式都安装到NIL
,它将评估第一个真值(一切不是{{1}})或NIL。在链接的练习2中,您将以类似的方式重写函数。
答案 2 :(得分:1)
这不是你被困的地方。你不得不设计策略,而不是编写代码。
你将不得不进行树搜索(你称之为“lisp对象”的东西实际上只是一个缺点 - “lisp对象”是一个误导性术语,因为在Lisp中,很多东西都是对象,而不仅仅是conses )。决定是进行广度优先搜索还是深度优先搜索,如何累积访问者路径,以及如何传达匹配或不匹配调用树。
答案 3 :(得分:1)
有时候处理一个稍微更普遍的问题会更容易一些,然后弄清楚如何将它专门化到手头的特定问题。在这种情况下,您将获得某种结构,以及可以访问该结构的子结构的许多访问者。给定要查找的元素和要搜索的内容,您可以通过检查事物是否是元素进行搜索,如果是,则返回到目前为止的路径(以适当的格式),如果不是,那么如果它是你可以用访问器分解的结构,尝试每个分解的部分。
(defun find-element (element structure structure-p accessors &key (test 'eql))
(labels ((fe (thing path)
"If THING and ELEMENT are the same (under TEST), then
return PATH. Otherwise, if THING is a structure (as
checked with STRUCTURE-P), then iterate through
ACCESSORS and recurse on the result of each one
applied to THING."
(if (funcall test thing element)
;; return from the top level FIND-ELEMENT
;; call, not just from FE.
(return-from find-element path)
;; When THING is a structure, see what
;; each of the ACCESSORS returns, and
;; make a recursive call with it.
(when (funcall structure-p thing)
(dolist (accessor accessors)
(fe (funcall accessor thing)
(list* accessor path)))))))
;; Call the helper function
;; with an initial empty path
(fe structure '())))
这将返回我们需要的访问器序列,它们需要以相反的顺序应用于结构。例如:
(find-element 'waldo '(ralph waldo emerson) 'consp '(car cdr))
;=> (CAR CDR)
因为(car (cdr '(ralph waldo emerson)))
是waldo
。同样
(find-element 'emerson '(ralph (waldo emerson)) 'consp '(first rest))
;=> (FIRST REST FIRST REST)
因为(first (rest (first (rest '(ralph (waldo emerson))))))
是emerson
。所以我们已经解决了获取访问者函数列表的问题。现在我们需要建立实际的表达式。使用reduce
:
(defun build-expression (accessor-path structure)
(reduce 'list accessor-path
:initial-value (list 'quote structure)
:from-end t))
只要我们提供结构,这就按照我们需要的方式工作。例如:
(build-expression '(frog-on bump-on log-on hole-in bottom-of) '(the sea))
;=> (FROG-ON (BUMP-ON (LOG-ON (HOLE-IN (BOTTOM-OF '(THE SEA))))))
(build-expression '(branch-on limb-on tree-in bog-down-in) '(the valley o))
;=> (BRANCH-ON (LIMB-ON (TREE-IN (BOG-DOWN-IN '(THE VALLEY O)))))
现在我们只需将这些放在一起:
(defun where-is-waldo? (object)
(build-expression
(find-element 'waldo object 'consp '(first rest))
object))
这就像我们想要的那样:
(where-is-waldo? '(ralph waldo emerson))
;=> (FIRST (REST '(RALPH WALDO EMERSON)))
(where-is-waldo? '(mentor (ralph waldo emerson) (henry david thoreau)))
;=> (FIRST (REST (FIRST (REST '(MENTOR (RALPH WALDO EMERSON) (HENRY DAVID THOREAU))))))