【发布时间】:2011-08-10 01:32:54
【问题描述】:
例如,如果我可以在命令式语言中使用数组或在 C++ 中使用映射(树结构),这将非常容易。在计划中,我不知道如何开始这个想法?谁能帮我解决这个问题?
谢谢,
【问题讨论】:
标签: scheme
例如,如果我可以在命令式语言中使用数组或在 C++ 中使用映射(树结构),这将非常容易。在计划中,我不知道如何开始这个想法?谁能帮我解决这个问题?
谢谢,
【问题讨论】:
标签: scheme
您的问题对计数的内容不是很具体。我假设您想创建某种元素的频率表。有几种方法可以解决这个问题。 (如果您使用的是 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 添加到 baz 的值,您创建一个排除 baz 的新 alist:
((foo . 1) (bar . 2))
然后重新添加baz 的新值:
((baz . 3) (foo . 1) (bar . 2))
第二步是exclude函数的作用,可能是函数中最复杂的部分。
更直接的方法是使用哈希表(来自 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)))
这种方法使用特定于 Racket 的哈希表(不同于 SRFI 69 的哈希表),它确实支持纯功能工作流。另一个好处是,这个版本也是三个版本中最简洁的。
(define (bagify lst)
(foldl (lambda (key ht)
(hash-update ht key add1 0))
#hash() lst))
您甚至可以为此使用 for 推导:
(define (bagify lst)
(for/fold ((ht #hash()))
((key (in-list lst)))
(hash-update ht key add1 0)))
这更多地表明了可移植 SRFI 69 散列库的缺点,而不是 Scheme 在执行纯功能任务方面的任何特定失败。有了合适的库,这项任务就可以轻松且功能性地实现。
【讨论】:
fold 的解决方案!:-O)
在 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 的类型,以及 list 的定义为空列表或一对两个事物,我们需要检查它是哪种列表:
;; count : Any List -> Int
(define (count what list)
(cond [(null? list) ...]
[else ...]))
第一种情况应该很明显:空列表中有多少what 项?
对于第二种情况,您知道它是一个非空列表,因此您有两条信息:它的头部(使用 first 或 car 获得)和尾部(使用 @ 获得987654332@或cdr):
;; count : Any List -> Int
(define (count what list)
(cond [(null? list) ...]
[else ... (first list) ...
... (rest list) ...]))
您现在只需要弄清楚如何结合这两条信息来获取代码。最后一点使它非常简单的信息是:由于(非空)列表的尾部本身就是一个列表,那么您可以使用count 来计算其中的内容。因此,您可以进一步得出结论,您应该在那里使用(count what (rest list))。
【讨论】:
cdr、car ...等。我正在寻找的是如何构建每个元素出现的列表。比如我遇到a,增加1,但是a应该存储在哪个结构中,怎么存储?
在像 Scheme 这样的函数式编程语言中,您必须有一些不同的想法,并利用构建列表的方式。不是通过增加索引来迭代列表,而是递归地遍历列表。您可以使用car(单个元素)删除列表的头部,您可以使用cdr(列表本身)获取尾部,您可以使用cons 将头部和尾部粘合在一起。你的函数大纲是这样的:
【讨论】:
在 Scheme 中,您通常使用 association lists 作为 O(n) 穷人的哈希表/字典。剩下的唯一问题是如何更新关联的元素。
【讨论】: