我刚刚开始学习球拍。
我编写了以下过程:
#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)
)
)
)
)
我没有在(list-ref lst i)
中使用do
,而是定义了一个变量l
:
(define (list-ref lst i) l)
但是似乎无法做到这一点,因为出现错误:
define:在表达式上下文中不允许使用:(define l(list-ref 首先))
但是define
中有很多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中定义变量?
答案 0 :(得分:1)
阅读your other question,我明白为什么您要编写 bounded 表达式-
…
(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))))
…
您显示的输入列表是–
(define lst
'((n 25 f +)
(s 25 m +)
(ll 20 no -)))
您的split
正在检查l
的每个元素的内容。 split
超越了它的界限,现在它 only 仅适用于包含此特定结构元素的列表。与set!
一起,else
中缺少cond
通常表示您做错了事。您也致电(cdr (last-element-of-list ...))
。如果last-element-of-list
返回一个原子,则cdr
会在此处抛出错误。
考虑以更通用的方式设计split
–
(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
过程–
(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
决定完全不同的命运–
(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 -))
请注意plus
和minus
是如何得到各自结果的。我们不必挑选中间的cons
结果。更直观地说,我们希望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
答案 1 :(得分:1)
do
循环不是惯用的球拍。它们是从Scheme继承的,无论出于何种原因,它们都不允许内部定义。我从未在Racket中使用过do
循环,因为for
的理解能力更强,并且通常更易于使用。另外,由于它们起源于球拍,而不起源于Scheme,因此它们支持您所期望的内部定义。
您可以使用split
而不是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
的明显重组之外,此代码还对您的代码进行了以下更改:
它使用last
中内置的racket/list
函数来获取列表的最后一个元素。
它使用equal?
而不是=
来比较符号,因为=
是专门用于比较数字的。
它可以正确缩进事物,并在惯用的位置加上括号。
答案 2 :(得分:-1)
我使用let
修复了您的代码,请阅读有关let
的文档,该文档在Scheme / Racket中大量使用。我最近没有使用Scheme,所以我无法像在文档中那样对其进行解释。
简而言之,它是本地符号定义/重新定义,您只能在let
正文中使用带值的符号。
关于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修复的代码
(define split
(lambda (list)
(let ((plus-list '())
(minus-list '()))
(cond ((null? list) '())
(else
(do ([i (length list) (- i 1)])
((zero? i))
(let ((l (list-ref list i))
(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))))))