我是函数式编程的新手,并尝试编写一个带有list参数的函数,如果列表包含每个符号长度为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)
)
)
然后根据结果我返回true或false。但我不知道如何迭代列表。我不想将列表转换为字符串并使用字符串函数。我们有任何类似“foreach”的陈述,或者循环来做我认为的事情吗?或任何其他建议?
注意:我还考虑过使用汽车,然后将其拆下并查看列表的其余部分,但由于我不知道长度,我不知道应该使用汽车多少次,即是否应该是汽车,caar,caaar等。
谢谢
答案 0 :(得分:2)
首先,让我们弄清楚一个lambda,当它的参数是长度为1的符号时将返回true,否则返回false:
(λ (x) (and (symbol? x) (= (string-length (symbol->string x)) 1)))
;; 'a -> #t, 'aa -> #f, '(a) -> #f
我们必须在那里使用字符串函数,因为符号'f
和符号'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
答案 1 :(得分:2)
我们是否有任何类似“foreach”的陈述,或者循环来做我认为的事情?
没有
或任何其他建议?
要迭代方案中的列表,您要么使用迭代列表的预先存在的函数(例如map
,filter
或fold-left
),要么您自己编写使用递归。根据您正在使用的Scheme方言,可能已经有一个函数(称为every
或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的数字相加(不使用filter
或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
不是布尔值,你需要将它转换为布尔值,但我可以'想到一个必要的场景。
答案 2 :(得分:2)
一种简单的方法,使用随时可用的功能 - 特别是,您可以在Racket中使用andmap
(或在R6RS中使用for-all
,或在SRFI-1中使用every
);如果列表中的所有元素都满足谓词,则将其视为 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
答案 3 :(得分: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时,您将通过寻找利用递归的机会而受益。