编写一个Racket函数
count-occurrences
,它使用两个符号列表并生成一个列表 测量第一个列表中的项目在第二个列表中出现的次数的自然数。例如:(count-occurrences (list 'a 'b 'a 'q) (list 'r 'a 'b 'e 'b 'g))
=> (清单1 2 1 0)
我一直在努力解决这个问题 - 如何使用map
来解决这个问题,因为对于这个问题我们指定的不能使用递归。
我最初的想法是做以下事情:
(define (count-occurrences los1 los2)
(map
(length (filter (lambda (x) (symbol=? x (first los1))) los2))
los1))
但是在这里使用length
只能得到数字' a而不是进入递归。对于抽象函数,内部函数只能有一个参数,所以我完全迷失了。
答案 0 :(得分:3)
如果... x ...
是开放公式,即引用未绑定变量x
的表达式,则将其包装在lambda
中} form使它成为x
中的一个函数,如下所示:
(lambda (x) ... x ... )
其中x
被该lambda表单绑定;这个所谓的lambda函数的参数,也就是说,lambda
形式引入的匿名函数。
因此,解决问题的方法非常简单:认识到
(length
(filter (lambda (x)
(symbol=? x (first los1)))
los2))
实际应该是
(length
(filter (lambda (x)
(symbol=? x y))
los2))
其中y
依次引用每个的los1
元素,不是只是第一个;并且它是y
中的开放式公式 - 也就是说,y
未绑定,免费。因此,我们必须捕获它,并使其绑定,通过...是的,将此表达式括在lambda
形式中,从而使其成为 y
中的函数!像这样:
(lambda (y)
(length
(filter (lambda (x)
(symbol=? x y))
los2)))
而此是通过los1
映射的内容。
通过这种简单的调整,您的代码将成为正确的工作函数定义。
答案 1 :(得分:1)
这是否符合您的要求和限制?
(define (count-occurrences lst1 lst2)
(map (lambda (e1)
(count (lambda (e2) (eq? e1 e2))
lst2))
lst1))
答案 2 :(得分:0)
跟踪密钥和值的好方法是使用哈希表。虽然可以使用count-occurrences
编写map
并传递lambda
,但显式可能会让您更容易看到正在发生的事情。
;;; list list -> list
;;;
(define (count-occurrences keys values)
;; Create data structure
(define ht (make-hash))
;; Initialize data structure with keys
;; Set the value of each key to zero
;; Since we have not started counting
(for ([k keys])
(hash-set! ht k 0))
;; Iterate over values and
;; Increment hash table if
;; When value is a key
(for ([v values])
(if (hash-has-key? ht v)
(hash-set! ht v (+ (hash-ref ht v) 1))
null))
;; Iterate over keys and
;; Create list of values
(for/list ([k keys])
(hash-ref ht k)))
由于禁止递归,显式循环可以使得比隐式循环更易于维护/读取代码。此外,for
的变化值得了解。散列表的优点是重复键读取相同的值,并且不需要两次跟踪相同的键。
使用for
而不是map
的一个工程优势是,更容易推断运行时间。此代码的运行时间为2m + n
,其中m
为keys
且n
为values
。使用map
的解决方案通常为m * n
。这没有什么本质上的错误。但值得认识的是。