因此,我编写了一个函数,用于按无序对的“值”(对的第二部分)对列表进行排序。这是我对递归函数的尝试(我知道它具有基本的设计):
*编辑功能设计:该功能的工作依据:
获取无序对列表,排序频率列表以及用于递归调用的可选startList。首先将listRet设置为等于startList。
如果无序列表仅包含一个元素,则将该元素推入listRet
。
如果该列表大于1,那么将遍历每对不规则列表,并检查其是否等于有序频率列表的第一个元素。
如果不是最后一个元素,则将其推送到listRet。
然后,循环继续进行,直到命中最后一个元素为止,然后以递归方式调用该函数,并从无序列表中删除被推入的对,因为已将其正确放置在listRet和最高频率中。 listRet作为可选的startList参数放置在适当的位置。
现在,如果无序列表包含多个元素,而最后一个元素是正确的排序频率,那么我选择将该元素移到列表的最前面并进行递归调用。
现在可以拉出无序列表的第一个元素,然后滥用do循环到达列表的末尾,并再次执行步骤5中的递归函数调用。
首先,如果无序列表的长度为1(如第2步),则退出if语句,并应返回listRet
。
功能:
(defun sort-by-freq (listUnord listOrdFreqs &optional startList)
(print startList)
(let ((listRet startList)) ;; step 1
(if (equal (length listUnord) 1) ;;step 2
;;(print listRet)
(push (car listUnord) listRet)
(loop for pair in listUnord ;;step 3
do (if (and (equal (cdr pair) (car listOrdFreqs))
(not (equal (last-element-by-pair listUnord)(car pair))))
(push pair listRet) ;;step 4
(if (and (equal (cdr pair) (car listOrdFreqs))
(equal (last-element-by-pair listUnord)(car pair)))
(sort-by-freq (append (list (cons (car pair) (cdr pair)))
(remove-element listUnord pair))
listOrdFreqs listRet) ;;step 6
(if (equal (last-element-by-pair listUnord)(car pair))
(sort-by-freq (remove-element listUnord (car listRet))
(cdr listOrdFreqs)
listRet)))))) ;; step 5
listRet)) ;;step 8
所以,如果我打电话给我
(sort-by-freq (list(cons 'c 2)(cons 'b 3)(cons 'a 1)) '(3 2 1))
我希望得到结果:
((A . 1) (C . 2) (B . 3))
但是由于某种原因,我只得到回报:
((B . 3))
使用(print startList)
语句,我可以确认startList正在按照我的期望进行构建。它输出:
NIL
((B . 3))
((B . 3))
((C . 2) (B . 3))
我已经通过注释掉的;;(print retList)
确认了在输出((C . 2) (B . 3))
之后达到了退出条件。 (push (car listUnord) listRet)
应该将第三个元素(A . 1)
推到列表的最前面,并返回listRet
。这与我使用输出设计其他功能的方式一致并且有效。
我想念什么?
*编辑这是我使用的两个辅助函数:
(defun remove-element (origPairList origPair)
(let ((retlist (list)))
(loop for pair in origPairList
do (if (not(equal(car origPair)(car pair)))
(push pair retlist)))
retlist))
(defun last-element-by-pair (pairList)
(let ((lastEl (car (car pairList))))
(if (not (null (cdr pairList)))
(loop for el in pairList
do (setf lastEl (car el))))
lastEl))
答案 0 :(得分:2)
一些提示...
简化事情:
(defun remove-element (origPairList origPair)
(let ((retlist (list)))
(loop for pair in origPairList
do (if (not(equal(car origPair)(car pair)))
(push pair retlist)))
retlist))
到
(defun remove-element (list pair)
(remove (car pair) list :key #'car))
和
(defun last-element-by-pair (pairList)
(let ((lastEl (car (car pairList))))
(if (not (null (cdr pairList)))
(loop for el in pairList
do (setf lastEl (car el))))
lastEl))
到
(defun last-element-by-pair (pair-list)
(caar (last pair-list)))
糟糕的汽车/ CDR混乱。在LOOP中使用解构。
(loop for pair in foo ... (car pair) ... (cdr pair) ... (car pair) ...)
是
(loop for (head . tail) in foo ... head ... tail ... head ...)
不要一直走着走
然后
(if (equal (length foo) 1) ...)
是
(if (and (consp foo) (rest foo)) ...) ; no need to traverse the list
其他问题:
您还需要确保代码正确缩进。通常,在编辑器中通过键盘输入即可。另外,您的代码缺少右括号。因此,该代码在语法上是不正确的。
(defun sort-by-freq (listUnord listOrdFreqs &optional startList)
(print startList)
(let ((listRet startList))
(if (equal (length listUnord) 1)
(push (car listUnord) listRet) ;; <- this makes no sense.
;; since you quit the function
;; there is no useful effect
(loop for pair in listUnord
do (if (and (equal (cdr pair) (car listOrdFreqs))
(not (equal (last-element-by-pair listUnord)(car pair) )))
(push pair listRet)
(if (and (equal (cdr pair) (car listOrdFreqs))
(equal (last-element-by-pair listUnord)(car pair)))
;; this call to sort-by-freq makes no sense.
;; you are not using the return value
(sort-by-freq ;; what are you appending here, only one list?
(append (list (cons (car pair) (cdr pair))
(remove-element listUnord pair)))
listOrdFreqs
listRet)
(if (equal (last-element-by-pair listUnord)(car pair))
;; this call to sort-by-freq makes no sense.
;; you are not using the return value
(sort-by-freq (remove-element listUnord (car listRet))
(cdr listOrdFreqs)
listRet))))))
listRet))
基本上在
(loop for e in list
do (compute-some-thing-and-return-it e))
调用该函数没有任何意义,因为未使用返回值。调用该函数的唯一原因是它具有副作用。
示例:
CL-USER 310 > (loop for e in '(1 2 3 4)
do (if (evenp e)
(* e 10)
(* e 100)))
NIL
如您所见,它返回NIL。可能不是您想要的。
答案 1 :(得分:2)
您正在do
子句中的循环内评估表达式。的
返回值永远不会在任何地方使用。
函数的返回值由局部变量listRet
给出,
设置在函数的开头,并在
2个地方,两个调用push
。第一个只发生在
输入大小为1的列表。第二次推送仅发生在步骤4。
我们可以很容易地看到所有其他操作均无效
在本地listRet
变量上。另外,因为sort-by-freq
因为您的辅助功能是纯净的(它们永远不会破坏
listRet
指向的列表结构),您还知道列表是
不会因为其缺点细胞的连接方式不同而被修改
随着时间的流逝。
让我们通过使用示例跟踪代码来确认这一点:
(trace sort-by-freq last-element-by-pair remove-element)
当您评估测试时,会发出以下跟踪信息(输出因实现而异,此处使用的是SBCL):
0: (SORT-BY-FREQ ((C . 2) (B . 3) (A . 1)) (3 2 1))
1: (LAST-ELEMENT-BY-PAIR ((C . 2) (B . 3) (A . 1)))
1: LAST-ELEMENT-BY-PAIR returned A
1: (LAST-ELEMENT-BY-PAIR ((C . 2) (B . 3) (A . 1)))
1: LAST-ELEMENT-BY-PAIR returned A
1: (LAST-ELEMENT-BY-PAIR ((C . 2) (B . 3) (A . 1)))
1: LAST-ELEMENT-BY-PAIR returned A
1: (REMOVE-ELEMENT ((C . 2) (B . 3) (A . 1)) (B . 3))
1: REMOVE-ELEMENT returned ((A . 1) (C . 2))
1: (SORT-BY-FREQ ((A . 1) (C . 2)) (2 1) ((B . 3)))
2: (LAST-ELEMENT-BY-PAIR ((A . 1) (C . 2)))
2: LAST-ELEMENT-BY-PAIR returned C
2: (LAST-ELEMENT-BY-PAIR ((A . 1) (C . 2)))
2: LAST-ELEMENT-BY-PAIR returned C
2: (LAST-ELEMENT-BY-PAIR ((A . 1) (C . 2)))
2: LAST-ELEMENT-BY-PAIR returned C
2: (REMOVE-ELEMENT ((A . 1) (C . 2)) (C . 2))
2: REMOVE-ELEMENT returned ((A . 1))
2: (SORT-BY-FREQ ((C . 2) (A . 1)) (2 1) ((B . 3)))
3: (LAST-ELEMENT-BY-PAIR ((C . 2) (A . 1)))
3: LAST-ELEMENT-BY-PAIR returned A
3: (LAST-ELEMENT-BY-PAIR ((C . 2) (A . 1)))
3: LAST-ELEMENT-BY-PAIR returned A
3: (REMOVE-ELEMENT ((C . 2) (A . 1)) (C . 2))
3: REMOVE-ELEMENT returned ((A . 1))
3: (SORT-BY-FREQ ((A . 1)) (1) ((C . 2) (B . 3)))
3: SORT-BY-FREQ returned ((A . 1) (C . 2) (B . 3))
2: SORT-BY-FREQ returned ((C . 2) (B . 3))
1: SORT-BY-FREQ returned ((B . 3))
0: SORT-BY-FREQ returned ((B . 3))
可以看到的一个小问题是
last-element-by-pair
被多次调用且输入相同,
是浪费。可以在循环之前调用一次。
在级别0处,该函数遍历对,直到找到(B . 3)
,
其频率等于第二个列表中的第一个频率。这是
步骤4,然后将配对推到由
调用listRet
的本地变量sort-by-freq
。
当循环到达无序列表的最后一个元素时,
用(i)新的无序对列表递归调用函数,
使用remove-element
进行计算,(ii)减少一个频率,以及(iii)当前
列表绑定到listRet
。
但是无论在递归步骤中发生什么,特别是无论递归调用产生什么结果,当前绑定
listRet
的作用域范围将不再被修改。另外,
listRet
当前指向的列表的结构未修改
(例如,使用nconc
或rplacd
)。
在2级及以下级别完成的所有工作都是关于将值推入
本地命名为listRet
的临时变量的前面,然后将其丢弃。
我想念什么?
数据从sort-by-freq
的递归调用流向
该函数的当前调用已损坏,您必须表达
当前结果(递归结果),或者您需要进行突变
事物(但是不鼓励这样做)。
获取无序对列表,已排序频率列表以及 可选的startList用于递归调用。首先设置listRet 等于startList。
根据您的规范,我将函数定义如下:
(defun sort-pairs-by-frequencies (pairs frequencies)
...)
是否需要其他参数进行递归 调用对于该函数的用户而言不是问题。那样 细节应保留为隐藏状态,除非您确实想要用户 才能将列表作为第三个参数传递。
如果无序列表仅包含一个元素,则将该元素压入 列出Ret。 [...]如果不是最后一个元素,则将其推送到 listRet。
遍历列表的函数通常只需要考虑两个 情况:空列表和非空列表。您考虑更多的事实 极端情况,例如一个元素的列表或检查您是否 元素是最后一个,是一个巨大的危险信号。有问题 需要具有复杂的基本条件,但这可以在一个条件下完成 更简单的方法。
除了多次致电last-element-by-pair
,还请注意
您在功能的不同点重复测试,即使
在给定的上下文中它们一定是正确的。
(if (and (equal (cdr pair) (car listOrdFreqs))
(not (equal (last-element-by-pair listUnord)(car pair))))
(push pair listRet) ;;step 4
(if (and (equal (cdr pair) (car listOrdFreqs))
(equal (last-element-by-pair listUnord)(car pair)))
(sort-by-freq (append (list (cons (car pair) (cdr pair)))
(remove-element listUnord pair))
listOrdFreqs listRet) ;;step 6
(if (equal (last-element-by-pair listUnord)(car pair))
(sort-by-freq (remove-element listUnord (car listRet))
(cdr listOrdFreqs)
listRet))))
给出适当的定义,可以将上面的内容写成:
(if (and A (not B))
(step-4)
(if (and A B)
(step-6)
(if B (step-5))))
让我们根据每个步骤写出每个步骤适用的条件 它们属于if表达式的树中的分支:
对于步骤4:(and a (not b))
必须成立,因为它位于if的“ then”分支中。
对于步骤5:(and b (not a))
,基于对路径谓词的以下简化:
(and b
(not (and a b))
(not (and a (not b))))
=> (and b
(or (not a) (not b))
(or (not a) b))
=> (and b (or (not a) nil) t)
=> (and b (not a))
对于步骤6:(and a b)
因此,您可以将测试编写为:
(cond
(a (if b (step-6) (step-4)))
(b (step-5)))
如果列表大于1,则每对不规则列表为 遍历并检查它是否等于 有序频率列表。
以上是算法的核心。对于每个频率F 将无序元素划分为两个列表:其中一个 频率等于F,其他等于。
让我们定义一下:如何根据是否要删除或保留项目 它们的频率匹配给定的频率。以下不是 一定有效,但它既有效又简单。而且,它会出错 通过仅使用不可变操作来保持谨慎:
(defun remove-frequency (pairs frequency)
(remove frequency pairs :test #'= :key #'cdr))
(defun keep-frequency (pairs frequency)
(remove frequency pairs :test-not #'= :key #'cdr))
(remove-frequency '((a . 20) (b . 10) (c . 5) (d . 20)) 20)
=> ((B . 10) (C . 5))
(keep-frequency '((a . 20) (b . 10) (c . 5) (d . 20)) 20)
=> ((A . 20) (D . 20))
然后,您的主要功能在频率上递归:
(defun sort-pairs-by-frequencies (pairs frequencies)
(if (null frequencies)
pairs
(destructuring-bind (frequency . frequencies) frequencies
(append (keep-frequency pairs frequency)
(sort-pairs-by-frequencies (remove-frequency pairs frequency)
frequencies)))))
当没有给定频率进行排序时,未排序的对列表为
回到。否则,frequencies
可以被分解为一个cons单元格,
其中第一项是frequency
,其余项是
frequencies
。请注意,frequencies
的先前绑定是
阴影,因为从这一点上我们不需要参考
整个频率列表了。
然后,一般情况下函数的返回值由下式计算
附加具有给定频率的对和列表
对与frequency
不匹配的对,根据
其余频率。
(sort-pairs-by-frequencies '((a . 20) (b . 10) (c . 5) (d . 20))
'(5 10 20))
=> ((C . 5) (B . 10) (A . 20) (D . 20))
通过首先评估(trace sort-pairs-by-frequencies)
,您还
获取以下跟踪:
0: (SORT-PAIRS-BY-FREQUENCIES ((A . 20) (B . 10) (C . 5) (D . 20)) (5 10 20))
1: (SORT-PAIRS-BY-FREQUENCIES ((A . 20) (B . 10) (D . 20)) (10 20))
2: (SORT-PAIRS-BY-FREQUENCIES ((A . 20) (D . 20)) (20))
3: (SORT-PAIRS-BY-FREQUENCIES NIL NIL)
3: SORT-PAIRS-BY-FREQUENCIES returned NIL
2: SORT-PAIRS-BY-FREQUENCIES returned ((A . 20) (D . 20))
1: SORT-PAIRS-BY-FREQUENCIES returned ((B . 10) (A . 20) (D . 20))
0: SORT-PAIRS-BY-FREQUENCIES returned ((C . 5) (B . 10) (A . 20) (D . 20))
编写上述函数是为了使它们易于阅读 和“平凡”正确,但它们仍在浪费内存和堆栈。其他方法也可以使用循环(恒定的堆栈使用量)对元素进行排序(不分配内存)。
答案 2 :(得分:0)
我用一个不太复杂的解决方案解决了这个问题。我承认这里的其他张贴者写出了更为优雅的解决方案,但它们与我的尝试无关。我想使用目前已经了解的基本语法来解决这个问题。
这是我的新sort-by-frequency
函数的样子:
(defun sort-by-frequency (pairs frequencies)
(let ((retList (list)))
(loop for freq in frequencies
do (push (extract-pair-match pairs freq (extract-keys retList)) retList))
retList))
我现在使用一个简单的循环遍历频率列表,然后使用函数extract-pair-match
根据频率找到匹配项,该函数还从变量{中获取键(extract-keys
) {1}},以便它可以搜索并确保同一键不会出现两次。
功能如下:
retList
首先,它检查列表(defun extract-pair-match(pairs match keys)
(if (and (equal (cdr(car pairs)) match) (not (search-keys keys (car(car pairs)))))
(car pairs)
(extract-pair-match (cdr pairs) match keys)))
的第一项,以查看其键是否与pairs
相匹配,然后使用match
列表上的函数search-keys
(在keys
的{{1}}处传递以确保该键尚未包含在列表中,如果它继续到下一个术语,则假定每个频率将有一个匹配项。
这是retList
函数:
sort-by-frequency
这是search-keys
函数:
(defun search-keys (keys match)
(if (null keys)
nil
(if (equal (car keys) match)
(car keys)
(search-keys (cdr keys) match))))
现在,如果我这样做:
extract-keys
我得到:
(defun extract-keys (pairList)
(let ((retlist (list (car (car pairList)))))
(loop for pair in (cdr pairList)
do (push (car pair) retlist))
retlist))