List 定义:在表达式上下文中不允许

List 定义:在表达式上下文中不允许,list,scheme,racket,List,Scheme,Racket,我刚开始学球拍 我编写了以下程序: #lang racket (define split (lambda (list) (define plus-list '()) (define minus-list '()) (cond ((null? list) '()) (else (do ([i (length list) (- i 1)]) ((zero? i))

我刚开始学球拍

我编写了以下程序:

#lang racket

(define split
   (lambda (list)
      (define plus-list '())
      (define minus-list '())

      (cond ((null? list) '())
            (else
               (do ([i (length list) (- i 1)])
                   ((zero? i))
                   (define l (list-ref list i))
                   (define item (last-element-on-list l))

                   (cond ((= (cdr l '+)) (set! plus-list (cons list plus-list)))
                         ((= (cdr l '-)) (set! minus-list (cons list minus-list))))
               )

               (cons plus-list minus-list)
            )
      )
   )
)
我没有在de中使用list ref lst i,而是定义了一个变量l:

但我似乎不能这样做,因为我得到了错误:

定义:不允许在:define l list ref中的表达式上下文中使用 一级

但是do内部有很多定义

如果我删除do中的所有定义,我必须编写大量代码,而且阅读和理解起来并不容易:

(define split
   (lambda (list)
      (define plus-list '())
      (define minus-list '())

      (cond ((null? list) '())
            (else
               (do ([i (length list) (- i 1)])
                   ((zero? i))

                   (cond ((= (cdr (last-element-on-list (list-ref list i)) '+)) (set! plus-list (cons (list-ref list i) plus-list)))
                         ((= (cdr (last-element-on-list (list-ref list i)) '-)) (set! minus-list (cons (list-ref list i) minus-list))))
               )

               (cons plus-list minus-list)
            )
      )
   )
)
如何在do中定义变量?

我使用let修复了您的代码,请阅读有关let的文档,它在Scheme/Racket中大量使用。我最近没有使用Scheme,所以我无法像文档中那样解释它

简而言之,它是本地符号定义/重新定义,您只能在let body中使用带值的符号

关于let的简短示例

(define x 5)

(let ((x 10))
  (display x)) # => 10

(display x) # => 5

(let ((y 1))
  (display y)) # => 1

(display y) # = => (error)  y: undefined
您的代码使用let修复

我明白你为什么写粗体字了-

…

(cond ((= (cdr (last-element-on-list (list-ref list i)) '+))
       (set! plus-list
             (cons (list-ref list i) plus-list)))

      ((= (cdr (last-element-on-list (list-ref list i)) '-))
       (set! minus-list
             (cons (list-ref list i) minus-list))))
…
您的拆分是检查l的每个元素的内容。split已经超越了它的边界,现在它只适用于包含此特定结构元素的列表。和布景一起!,一秒钟内缺少其他东西通常表明你做错了什么。您还可以调用列表的最后一个元素cdr。。。。若列表的最后一个元素返回一个原子,cdr将在这里抛出一个错误

考虑以更通用的方式设计拆分-

(define (split proc l)
  (define (loop l true false)
    (cond ((null? l)
           (cons true false))
          ((proc (car l))
           (loop (cdr l)
                 (cons (car l) true)
                 false))
          (else
           (loop (cdr l)
                 true
                 (cons (car l) false)))))
  (loop l '() '()))

(split (lambda (x) (> x 5))
       '(1 5 3 9 7 0 8 3 2 6 4))

;; '((6 8 7 9) 4 2 3 0 3 5 1)
如果我们的列表包含不同的元素,我们仍然可以使用相同的拆分程序——

(split (lambda (x) (eq? '+ (cadr x)))
       '((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +)))
;; '(((4 +) (3 +) (2 +) (1 +)) (3 -) (2 -) (1 -))
我认为现在开始学习传接球的风格永远都不为时尚早。下面,return表示我们的继续,默认为cons,这与我们在原始实现中用于返回最终结果的过程相同。直观地说,延拓表示计算的下一步——

(define (split proc l (return cons)) ;; `return` is our continuation
  (if (null? l)
      ;; base case: list is empty, return empty result
      (return '() '())
      ;; inductive case: at least one `x`
      (let* ((x (car l))
             (bool (proc x)))
        (split proc          ;; tail recur with our proc
               (cdr l)       ;; ... a smaller list
               (lambda (t f) ;; ... and "the next step"
                 (if bool                        ;; if `(proc x)` returned true
                     (return (cons x t)          ;; ... cons the `x` onto the `t` result
                             f)                  ;; ... and leave the `f` the same
                     (return t                   ;; otherwise leave `t` the same
                             (cons x f))))))))   ;; ... and cons the `x` onto the `f` result
如果我们运行split过程,您会注意到我们得到与上面相同的精确输出。乍一看,我们似乎把一个不错的程序搞得一团糟,但是这种实现有一个明显的优点。因为延续是用户可配置的,而不是cons,我们可以为两个列表t和f决定完全不同的命运-

注意加号和减号是如何给出相应结果的。我们不必分出一个中间结果。更直观地说,我们希望printf是下一步,但我们只需要指定第一个参数——

(split (lambda (x) (eq? '+ (cadr x)))
       '((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +))
       (curry printf "plus: ~a, minus: ~a\n"))
;; plus: ((1 +) (2 +) (3 +) (4 +)), minus: ((1 -) (2 -) (3 -))
现在我们已经触及了函数式风格的表面:D

do循环不是惯用的拍子。它们是从Scheme继承的,无论出于什么原因,它们都不允许内部定义。我从未在球拍中使用过do循环,因为for理解功能更强大,而且通常更容易使用。另外,由于它们起源于Racket,而不是Scheme,所以它们支持您所期望的内部定义

您可以使用for/fold而不是do来编写分割函数,这具有不需要使用set的额外优势!避免了使用list-ref而不是遍历列表的二次访问时间。我不完全确定split函数应该做什么,因为即使删除了内部定义,它也不会编译,但下面是我对您可能尝试做的最好猜测:

(define (split lst)
  (for/fold ([plus-lst '()]
             [minus-lst '()])
            ([l (in-list lst)])
    (define item (last l))
    (cond
      [(equal? item '+)
       (values (cons l plus-lst) minus-lst)]
      [(equal? item '-)
       (values plus-lst (cons l minus-lst))]
      [else
       (values plus-lst minus-lst)])))
除了使用for/fold而不是do进行明显的重组外,此代码还对代码进行了以下更改:

它使用racket/list中内置的last函数获取列表的最后一个元素

它使用的是相等的?而不是=来比较符号,因为=专门用于比较数字

它正确地缩进事物,并在惯用的位置放置紧括号


好啊谢谢当我们开始学习一门语言时,这就是问题所在,有很多事情我们都不知道,比如let。没问题,我对let做了一些简短的解释,我希望它能帮助你更多地理解它。但是你的let加列表“减列表”是错误的,有一个缺失,如果我添加了,我会得到以下错误:let:糟糕的语法缺少绑定对或body in:let plus list quote减list quote@VansFannel我刚刚尝试了我发布的答案的最新编辑,并且一切都正常工作,可能当你复制它时,你遗漏了一些括号。我在一个新的DrRacket窗口中复制了所有内容,添加了lang racket,我在列表l的最后一个元素的项中得到了错误:l:未绑定标识符。
(define (split proc l (return cons)) ;; `return` is our continuation
  (if (null? l)
      ;; base case: list is empty, return empty result
      (return '() '())
      ;; inductive case: at least one `x`
      (let* ((x (car l))
             (bool (proc x)))
        (split proc          ;; tail recur with our proc
               (cdr l)       ;; ... a smaller list
               (lambda (t f) ;; ... and "the next step"
                 (if bool                        ;; if `(proc x)` returned true
                     (return (cons x t)          ;; ... cons the `x` onto the `t` result
                             f)                  ;; ... and leave the `f` the same
                     (return t                   ;; otherwise leave `t` the same
                             (cons x f))))))))   ;; ... and cons the `x` onto the `f` result
(split (lambda (x) (eq? '+ (cadr x)))
       '((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +))
       (lambda (plus minus)
          (printf "plus: ~a, minus: ~a\n" plus minus)))
;; plus: ((1 +) (2 +) (3 +) (4 +)), minus: ((1 -) (2 -) (3 -))
(split (lambda (x) (eq? '+ (cadr x)))
       '((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +))
       (curry printf "plus: ~a, minus: ~a\n"))
;; plus: ((1 +) (2 +) (3 +) (4 +)), minus: ((1 -) (2 -) (3 -))
(define (split lst)
  (for/fold ([plus-lst '()]
             [minus-lst '()])
            ([l (in-list lst)])
    (define item (last l))
    (cond
      [(equal? item '+)
       (values (cons l plus-lst) minus-lst)]
      [(equal? item '-)
       (values plus-lst (cons l minus-lst))]
      [else
       (values plus-lst minus-lst)])))