1

我是函数式编程的新手,并尝试编写一个函数,该函数接受一个列表参数,如果列表由每个符号长度为 1 的符号组成,则返回 true。更具体地说,

;(sequence? '(a b c)) ----> true
; (sequence? '(aa b c)) ---> false since aa has length 2
; (sequence? '(a 1 c)) ----> false since 1 is not a symbol
; (sequence? '(a (b c))) --> false since (b c) is not a symbol

我正在考虑执行以下操作:对于列表中的每个符号,我检查它是否是符号并且长度为 1。

(define sequence?
      (lambda (inSeq)
      (if ( for each item in the list inSeq, all are symbols and length=1) #t #f)

      )
)

然后根据结果我返回真或假。但我不知道如何遍历列表。我不想将列表转换为字符串并使用字符串函数。我们有没有像“foreach”或for循环这样的语句来做我认为的事情?或任何其他建议?

注意:我也想过用car然后把它去掉,然后看列表的其余部分,但是由于我不知道长度,我不知道我应该用多少次car,即是否应该是car, caar、caaar 等

谢谢

4

4 回答 4

2

一种简单的方法,使用现成的功能 - 特别是,您可以andmap在 Racket(或for-allR6RS 或everySRFI-1)中使用;如果列表中的所有元素都满足谓词,则将其视为返回的foreach 。#t这种解决方案更符合函数式编程的精神,因为它使用通用的高阶过程通过将现有解决方案与其他子问题相结合来解决新问题。换句话说,我们不会重新发明轮子:

(define (sequence? seq)      ; `seq` is a sequence if
  (andmap (lambda (e)        ; it's true for all its elements that
            (and (symbol? e) ; each element is a symbol
                 (= 1 (string-length (symbol->string e))))) ; with length one
          seq))

请注意代码是如何准确表达其含义的:如果对于它的所有元素,列表是一个“序列”,那么每个元素都是长度为 1 的符号。为了确定一个符号的长度,我们首先将它转换成一个字符串,我们可以很容易地检查它是否满足长度要求。它按预期工作:

(sequence? '(a b c))
=> #t
(sequence? '(aa b c))
=> #f
(sequence? '(a 1 c))
=> #f
(sequence? '(a (b c)))
=> #f
于 2013-04-30T13:53:26.900 回答
2

如果您已经解决了列表的第一个元素的问题,那么您只需对其余元素重新应用相同的过程即可解决列表的其余元素的问题。

对于第一个元素,您需要以下内容:

(define (is-symbol-1 thing)
  (and (symbol? thing)
       (= 1 (string-length (symbol->string thing))))

接着

(define (sequence? list)
  (or (null? list)                     ;; #t if list is empty
      (and (is-symbol-1 (car list))    ;; first element is-symbol-1
           (sequence?   (cdr list))))) ;; and rest is sequence? too

这是递归的一个例子。当您学习 Scheme 时,您将通过寻找利用递归的机会而受益。

于 2013-04-30T14:03:38.787 回答
2

首先,让我们找出一个 lambda,当它的参数是长度为 1 的符号时返回 true,否则返回 false:

(λ (x) (and (symbol? x) (= (string-length (symbol->string x)) 1)))

;; 'a -> #t, 'aa -> #f, '(a) -> #f

'f我们必须在那里使用字符串函数,因为 symbol和 symbol之间没有任何有意义的区别'foo,除了它们的字符串表示形式。

现在,让我们使用该 lambda 表达式并使用它来过滤列表中的坏元素:

(filter (λ (x) (and (symbol? x) (= (string-length (symbol->string x)) 1))) 
        '(a b c))

;; '(a b c) -> '(a b c), '(a 2 c) -> '(a c), '(a bb c) -> '(a c)

现在,让我们检查以确保没有过滤掉任何内容,即原始列表的每个元素都是长度为 1 的符号。我们通过检查输出列表的长度是否与输入列表的长度相同来做到这一点。

(define (symbols-of-length-1 seq)
  (= (length (filter (λ (x) (and (symbol? x) (= (string-length (symbol->string x)) 1))) 
             seq))
     (length seq)))

;; '(a b c) -> #t, '(a 2 c) -> #f, '(a (b) c) -> #f, '(a bb c) -> #f
于 2013-04-30T13:42:41.910 回答
2

我们有没有像“foreach”或for循环这样的语句来做我认为的事情?

不。

或任何其他建议?

要迭代方案中的列表,您可以使用迭代列表的预先存在的函数(例如mapfilterfold-left),或者使用递归编写自己的函数。根据您使用的 Scheme 方言,可能已经有一个函数(称为everyor andmap)接受一个列表和一个条件,并#t在列表中的每个项目的条件为真时返回。否则,您必须以递归方式或将其编写为折叠(尽管并非所有 Scheme 方言都具有折叠功能)。

遍历列表的递归函数通常如下所示:

(define (do-something-with-list lst)
  (if (null? lst)
    (handle-the-case-that-list-is-empty)
    (combine
      (some-transformation-on (car lst))
      (do-something-with-list (cdr lst)))))

例如,要将列表中大于 5 的所有数字相加(不使用filteror fold-left),您可以编写:

(define (sum-all-numbers>5 numbers)
  (if (null? numbers)
    ; Sum of the empty list is 0
    0 
    (+
      ; If the head of the list is > 5, add the number to the result, else
      ; add 0
      (if (> (car numbers) 5) (car numbers) 0)
      (sum-all-numbers>5 (cdr numbers)))))

您可以使用相同的方法来定义您的函数。

PS:(if condition #t #f)是多余的——你可以写condition(除非condition是布尔值以外的东西,你需要将它转换为布尔值,但我想不出有必要这样做的场景)。

于 2013-04-30T13:43:22.787 回答