如果我可以在命令式语言中使用数组或在C ++中使用map(树形结构),这非常容易。在计划中,我不知道如何开始这个想法?任何人都可以帮我吗?
谢谢,
答案 0 :(得分:9)
你的问题对于计算的内容并不十分具体。我假设您想要创建某种元素的频率表。有几种方法可以解决这个问题。 (如果您使用的是Racket,请向下滚动到底部以获取我的首选解决方案。)
此方法使用关联列表(alist)来保存元素及其计数。对于传入列表中的每个项目,它会在alist中查找该项目,并增加其存在的值,或者如果不存在则将其初始化为1.
(define (bagify lst)
(define (exclude alist key)
(fold (lambda (ass result)
(if (equal? (car ass) key)
result
(cons ass result)))
'() alist))
(fold (lambda (key bag)
(cond ((assoc key bag)
=> (lambda (old)
(let ((new (cons key (+ (cdr old) 1))))
(cons new (exclude bag key)))))
(else (let ((new (cons key 1)))
(cons new bag)))))
'() lst))
增量是有趣的部分。为了实现纯函数,我们实际上不能更改alist的任何元素,而是必须排除正在更改的关联,然后将该关联(使用新值)添加到结果中。例如,如果您有以下列表:
((foo . 1) (bar . 2) (baz . 2))
并想要将{1}的值加1,您创建了一个排除baz
的新列表:
baz
然后重新添加((foo . 1) (bar . 2))
的新值:
baz
第二步是((baz . 3) (foo . 1) (bar . 2))
函数的作用,可能是函数中最复杂的部分。
更直接的方法是使用哈希表(来自SRFI 69),然后为列表的每个元素逐个更新它。由于我们直接更新哈希表,因此它不是纯函数。
exclude
这种方法使用特定于Racket的哈希表(与SRFI 69的哈希表不同),它们支持纯功能工作流。另一个好处是,这个版本也是三者中最简洁的。
(define (bagify lst)
(let ((ht (make-hash-table)))
(define (process key)
(hash-table-update/default! ht key (lambda (x) (+ x 1)) 0))
(for-each process lst)
(hash-table->alist ht)))
你甚至可以使用(define (bagify lst)
(foldl (lambda (key ht)
(hash-update ht key add1 0))
#hash() lst))
理解:
for
这更多地表明了便携式SRFI 69哈希库的缺点,而不是任何特定的用于执行纯功能任务的Scheme失败。使用正确的库,可以轻松实现此任务。
答案 1 :(得分:4)
在Racket中,你可以做到
(count even? '(1 2 3 4))
但更严重的是,使用Scheme中的列表执行此操作比您提到的更容易。列表为空,或者是一对包含第一个项目和其余项目。在代码中遵循该定义,您将使其“自行编写”。
这是一个基于HtDP开头的提示(这是一本很好的书,可以了解这些事情)。从函数“header”开始 - 它应该接收一个谓词和一个列表:
(define (count what list)
...)
添加输入的类型 - what
是一些值,list
是一个列表:
;; count : Any List -> Int
(define (count what list)
...)
现在,鉴于list
的类型,以及列表的定义为空列表或两对内容,我们需要检查它是哪种列表:
;; count : Any List -> Int
(define (count what list)
(cond [(null? list) ...]
[else ...]))
第一种情况应该是显而易见的:空列表中有多少what
个项目?
对于第二种情况,您知道它是一个非空列表,因此您有两条信息:它的头部(使用first
或car
获得)及其尾部(其中)你得到rest
或cdr
):
;; count : Any List -> Int
(define (count what list)
(cond [(null? list) ...]
[else ... (first list) ...
... (rest list) ...]))
现在您只需要弄清楚如何组合这两条信息来获取代码。最后一点非常简单的信息是:由于(非空)列表的尾部本身就是一个列表,因此您可以使用count
来计算其中的内容。因此,您可以进一步得出结论,您应该在那里使用(count what (rest list))
。
答案 2 :(得分:2)
在像Scheme这样的函数式编程语言中,你必须有所不同,并利用列表的构建方式。不是通过递增索引来迭代列表,而是递归地遍历列表。您可以使用car
(单个元素)删除列表的头部,您可以使用cdr
(列表本身)获得尾部,并且可以将头部及其尾部与{{1}粘合在一起}。你的功能大纲是这样的:
答案 3 :(得分:0)
在Scheme中,您通常使用association lists作为O(n)穷人的哈希表/字典。唯一剩下的问题是如何更新相关元素。