Wheres-waldo在LISP中起作用

时间:2014-02-22 18:25:21

标签: recursion lisp acl2

我正在尝试解决LISP上的问题,我很多天都遇到了这个问题。

"编写一个名为wheres-waldo的函数,它将一个lisp对象(即由conses构建的数据结构)作为参数,并返回一个从该对象中提取符号waldo的Lisp表达式,如果它是本"

例如,

例如:(wheres-waldo'(emerson ralph waldo)) =

OUTPUT: (FIRST (REST (REST '(EMERSON RALPH WALDO))))
(wheres-waldo'(导师(ralph waldo emerson)(亨利·大卫·梭罗))) =

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找到了这个问题。 任何帮助,将不胜感激。谢谢。

4 个答案:

答案 0 :(得分:3)

您需要遍历列表,如果元素是列表,则递归到子列表中,就像实现深度搜索一样。唯一的区别是,为了产生所需的输出,你需要继续使用s-expression来回溯你用来实现的功能。

这是一种可能的实现方式。请注意,我使用了更传统的carcdr而不是firstrest - 它们是等效的。

(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),除了它是返回的表达式的值。如果acccurwaldo将确保我们返回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))))))