Scheme中的未绑定变量

时间:2014-10-11 21:26:47

标签: variables functional-programming scheme lisp scope

我知道自己想做什么,但我很难到达那里。我正在寻找一些指导。我或多或少地强迫我想做什么,并且必须有一种比我试图创建这个功能的方式更好的方法。 我目前在调用(set! nadj)(set! count)时收到未绑定的变量错误。

我正在尝试创建用户输入句子的功能。如果该句子的25%以上由形容词组成,则该函数返回false。

这是我到目前为止所做的:

(define OK
  (lambda (x)
    (cond
      ((adj? (car x))
       (set! count (+ count 1)))
      ((not (adj? (car x))       
            (set! nadj (+ nadj 1))))
      ((not (null? (OK (cdr x)))))                  
      ((null? x)
       (set! sum (+ nadj count)))
      ;;(set! div (/ count sum))
      ;;(* 100 div)
      ;;(< div 25))
      ((else  #f)))))

我要做的是为一个形容词和一个不是的单词的计数器制作一个计数器。然后我试图添加所有单词并将它们除以形容词的单词数量。然后我想将它乘以100,如果小于25%则返回true。我不是在寻找答案,或多或少只是想要一些指导。

如果您需要查看它,可以使用adj?函数。

(define adjectives '(black brown fast hairy hot quick red slow))
(define adj?
  (lambda(x)
   (if ( member x adjectives) #t #f)))

我确定这不是正常的Scheme表示法。我用C ++和Java编写了很多程序,我很难过渡到Scheme。

3 个答案:

答案 0 :(得分:2)

你说的是你的解决方案不是惯用的方案 - 我们非常努力避免变异,所有这些set!操作都不赞成:我们并不真正需要它们。一个更惯用的解决方案是将计数器作为参数传递,如@ uselpa的答案所示。他的解决方案使用名为let的显式递归。

我们可以更进一步 - 函数式编程的真正精神是重用现有的高阶程序并以解决我们问题的方式组合它们。我不知道您正在使用哪种Scheme解释器,但在Racket中,OK过程可以表达为:

(define (OK x)         ; assuming a non-empty list
  (< (/ (count adj? x) ; count the number of adjectives
        (length x))    ; divide by the total number of words
     0.25))            ; is it less than 25%?

如果您的计划口译员没有提供count程序,请从SRFI-1导入;也很容易实现自己的 - 再次,这是函数式编程的精神:我们希望构建一些本身有用的通用过程,并且可以在其他环境中轻松地重用和组合:

(define (count pred lst)
  (let loop ((lst lst) (counter 0))
    (cond ((null? lst) counter)
          ((pred (car lst)) (loop (cdr lst) (+ 1 counter)))
          (else (loop (cdr lst) counter)))))

扮演恶魔的主张可以使用命令式修复你的功能,只要我们首先定义变量(顺便说一下,这会导致&#34;未绑定变量&#34 ;错误) - 例如,在循环函数之前放置一个let:将其视为在递归开始之前发生的变量声明。另请注意,空列表大小写必须首先出现,以避免访问空列表中的元素,并且不要忘记在每一步推进递归。这很难看,但应该有效:

(define (OK x) ; assuming a non-empty list
  ; declare the counters outside the function
  (let ((adj 0) (nadj 0))
    ; looping function
    (let loop ((x x))
      (cond
         ; is the list empty?
        ((null? x)
         ; is the number of adjectives less than 25%?
         (< (/ adj (+ adj nadj)) 0.25))
         ; is current element an adjective?
        ((adj? (car x))
         ; increment adj counter
         (set! adj (+ adj 1))
         ; always advance recursion
         (loop (cdr x)))
         ; is current element anything other than an adjective?
        (else       
         ; increment nadj counter
         (set! nadj (+ nadj 1))
         ; always advance recursion
         (loop (cdr x)))))))

答案 1 :(得分:1)

我不知道你是否熟悉名为let ,但这在这里派上用场了:

(define (OK x)
  (let loop ((x x) (adj 0) (nadj 0)) ; named let
    (cond
      ((null? x)      (< (/ adj (+ adj nadj)) 0.25))
      ((adj? (car x)) (loop (cdr x) (+ 1 adj) nadj))
      (else           (loop (cdr x) adj (+ 1 nadj))))))

这是以下等效代码的便捷表示法:

(define (OK x)
  (define (loop x adj nadj)
    (cond
      ((null? x)      (< (/ adj (+ adj nadj)) 0.25))
      ((adj? (car x)) (loop (cdr x) (+ 1 adj) nadj))
      (else           (loop (cdr x) adj (+ 1 nadj)))))
  (loop x 0 0))

基本上我们定义了一个内部函数,而C ++和Java等语言中的循环变成了一个递归调用(并且为了增加混乱,递归调用的过程有时被称为loop ,如我的例子)。由于调用是在尾部位置完成的,因此这在Scheme中与您提到的语言中的经典循环一样高效。

通过修改递归调用的参数来替换变量赋值,即在这种简单的情况下通常找不到set!个过程。

编辑使用set!的示例实现:

(define OK
  (let ((adj 0) (nadj 0))
    (lambda (x)
      (cond
        ((null? x) (< (/ adj (+ adj nadj)) 0.25))
        (else      (if (adj? (car x))
                       (set! adj (+ 1 adj))
                       (set! nadj (+ 1 nadj)))
                   (OK (cdr x)))))))

答案 2 :(得分:0)

您无法设置未绑定的变量,即使是全局变量。变量指的是位置;设置一个不存在于任何地方的变量是不可能的:

(设置!a 1)
;未绑定变量:a ; a尚未提及任何地点
(定义a)
;价值:a (列表a)
;未分配的变量:a ;现在确实如此,但它还没有分配值
(设置!a 1)
;价值:a (列表a)
;价值:(1)
(设置!a 2)
;价值:1
(列表a)
;价值:(2)
功能

本地化封装的突变没有任何问题。根据定义,设置全局变量不是本地化的。

您应该为要使用的变量创建本地绑定(位置)。基本迭代built-in form do为您完成:

(define (OK x) 
  (do ((adj 0) (nadj 0))
      ((null? x)          ; loop termination condition
         (< (/ adj (+ adj nadj))
            0.25))        ; return value form
    ; loop body
    (if (adj? (car x))  
        (set! adj (+ adj 1))
        ; else
        (set! nadj (+ nadj 1)))
    ; some other statements maybe...
    ))

另一种选择有时可能会派上用场。当然,最惯用的Scheme代码是使用命名的 let 构造。它还会强制您重构可能使用do编写的意大利面条代码。别。 :)