球拍 - 搜索树中的递归(回溯)

时间:2015-04-14 14:22:24

标签: racket

我的球拍语言有问题。我想在列表中找到目标状态。但是在达到限制的那一刻,我作为参数得到了 - > 函数调用:预期在打开括号后的函数,但收到空。问题在于expand2。

如何在Racket中写这个?

(define (main start limit)  
  (let ([result (expand start limit)])
    (write result)))


(define (expand state limit)
  (cond ((goal state) (list state))
        ((< limit 0) '())
        (else 
         (let ([children (next-states state)]) ;;next states return a list of the kind ((4 3) (2 3))
           (let ((result (expand2 children (- limit 1))))
             (and result (cons state result))))  ;;(append result (cons state result))))) 


         (define (expand2 states limit)                                        
           (if (null? states) 
               (list states) 
               ((expand (car states) limit) 
                (expand2(cdr states) limit))))


(define (next-states state)
    (remove-null
    (list
        (fill-first state)   ;;if state is '(2 3) -> '(4 3) 
        (fill-second state)  ;; and so on
        (pour-first-second state)
        (pour-second-first state)
        (empty-first state)
        (empty-second state)))) 

由于

编辑:我正在尝试编写水壶问题,你需要有2加仑的第一个水壶,你有4加仑和3加仑容量的未测量的水壶。例如,要使用(main'(4 3)7)启动程序。 7是树的极限深度

4 个答案:

答案 0 :(得分:1)

expand2

中备用分支中的表达式
((expand (car states) limit)
 (expand2 (cdr states) limit))

表示您尝试将递归调用的结果作为函数调用expand,但据我所知,expand永远不会返回函数。

你真正想在这个分支中做什么?

修改

虽然我仍然不完全理解您当前的代码应该如何工作,但我认为如何使用递归实现回溯的一些说明可以帮助您:

递归调用应该能够进入当前状态之后的所有可能分支。每次递归调用返回时,检查它是否返回了有效的最终结果 - 在这种情况下,您只需返回到下一个更高级别 - 或者如果它是一个表示失败的特殊结果 - 在这种情况下,您进行下一次递归调用,如果当前状态仍然存在分支,或者如果您已用尽可能的分支,则返回特殊结果。

编辑2:

这是一些示例代码,应该有希望澄清我上面解释的关于如何使用递归进行回溯的基本思想。值得注意的是,Racket标准库函数ormap可以为我们完成很大一部分工作:

(define (backtrace state)
  (if (goal state)
      (list state)
      (let [[result (ormap backtrace (next-states state))]]
        (and result (cons state result)))))

答案 1 :(得分:1)

搜索

使用回溯搜索嵌套结构[graph]通常需要队列或堆栈或类似结构来保存临时状态。在Lisps中,堆栈很简单,因为很容易使用cons将元素推送到列表的前面,并使用firstcar访问列表的第一个元素并获取带有restcdr的“弹出”列表。使用list as a stack可提供depth first search

处理状态

在函数式中处理状态的标准方法是将状态作为附加参数传递。一个典型的方法是定义一个内部函数,它接受附加参数,然后用trampoline调用它。

示例代码

这是基于我对问题总体目标的理解。具体的输入和输出可根据您的需要进行调整:

#lang racket

(define (main start limit)

  (define (success? item limit)
    (equal? item limit))

  (define (inner start limit stack)
    ;; list? any? list? -> any? or null?
    ;; Returns limit on match, null on no match
    (cond 
      ;; There's nothing left
      [(and (empty? start)
            (empty? stack))
       null]
      ;; A list has been exhausted
      ;; So backtrack using the stack
      ;; As start state
      [(empty? start)
       (inner stack limit null)]
      ;; Otherwise look at the first item
      [else
       (let ((item (car start)))
         ;; Does the item match the limit?
         ;; **Remember the limit could be a list**
         (cond 
           ;; If it matches return the matching value
           [(success? item limit) item]
           ;; Otherwise if the item is a list
           ;; Push the elements of item onto the stack
           [(list? item)
            (inner (rest start)
                   limit
                   (append item stack))]
           ;; The item isn't a list and it doesn't match.
           [else (inner (rest start)
                        limit
                        stack)]))]))

  ;; Trampoline the inner function
  (inner start limit null))

样本输出

> (main '(1 2 3 (4 5) 6) '(4 5))
'(4 5)
> (main '(1 2 3 (4 5) 6) 6)
6
> (main '(1 2 3 (4 5) 6) 4)
4
> (main '(1 2 3 (4 5) 6) 8)
'()

答案 2 :(得分:0)

如果(展开(汽车状态)限制)返回空,则

( (expand (car states) limit) 
        (expand2(cdr states) limit))

(empty ...)相同,您将收到错误function call: expected a function after the open parenthesis, but received empty

答案 3 :(得分:0)

Racket有一个名为DrRacket的非常好的IDE。它将根据代码结构识别您正在编写的代码。按 CTRL + i 将在您编辑代码时为您“修复”标识,因此请经常按下它。 LISPy语言的Inidented代码不可读

从您的原始格式中,我不清楚expand2的定义是否在expand内的非法位置(本地定义位于顶部),但如果有适当的缩进,您会看到它在那里

你的代码中有很多奇怪的东西。一个是缺少括号,使得该程序从一开始就无效。

另一个原因是你有两个应用程序本身被括号括起来:

((expand (car states) limit) (expand2 (cdr states) limit))

如果将其与例如比较。

(- 10)

您看到(expand (car states) limit)需要成为一个过程,就像变量-成为一个过程一样。在这里你要忘了一个操作数。例如。 (cons expr-1 expr-b)或者如果有副作用,您可能需要(begin expr-1 expr-2)

一旦您的应用程序没有语法错误,DrRacket就有一个相当不错的调试器。您可以单步执行代码并查找其他错误。试一试!