我有这个名字和不同语言的列表
(setq l '((david spanish german)
(amanda italian spanish english)
(tom german french)))
我想用函数做下一个:对于每种语言,我需要与每种语言相关的每个名字。
例如,如果我使用列表L:
调用该函数(lenguages L)
我想表明这一点:
( (english (amanda))
(spanish (david amanda))
(italian (amanda))
(german(david tom))
(french(tom))
)
我知道如何做到这一点,但它只显示了一个项目。
(defun lenguages(names)
(cond((null names) nil)
((list (cadar names) (list (caar names))))))
这最后一个功能只显示(spanish (david))
答案 0 :(得分:3)
像这样的基于迭代的任务最适合Common Lisp非常强大的loop
宏。您可以在the GigaMonkeys book中阅读有关此宏的所有详细信息,但我们会在此处查看此问题所需的部分。让我们从函数定义开始。
(defun lenguages (names)
...)
在此内容中,我们希望迭代提供的列表。我们还想收集一些密钥,因此哈希表会很有用。散列表(在许多其他语言中称为映射或dicts)以时间有效的方式将键与值相关联。
(loop with hash = (make-hash-table)
for entry in names
for name = (car entry)
do ...
finally ...)
loop
宏非常强大,并且拥有自己的语言。 with
子句声明了一个局部变量,在本例中是一个哈希表。第一个for
定义了一个迭代变量。循环将以entry
绑定到names
的每个条目运行,并在条目用完时停止。第三行是另一个局部变量,但与with
不同,for
变量每次都会反弹,因此在每次迭代时name
将成为entry
的第一个元素。 do
块包含将在每次迭代时执行的任意Lisp代码,finally
包含要在循环结束时执行的Lisp代码块。
在do
块中,我们希望将人名添加到他们所知道的每种语言的哈希表条目中,因此我们需要另一个loop
来遍历已知语言。
(loop for lang in (cdr entry)
do (push name (gethash lang hash)))
此循环进入外部的do
块内。对于人的已知语言列表中的每种语言,我们希望将该人的名称添加到该语言的哈希值之前。通常,我们必须考虑散列键不存在的情况,但幸运的是,如果散列键不存在,Common Lisp默认为nil
,并且前面添加一个元素nil
创建一个单元素列表,这正是我们想要的。
现在,当完成此循环时,哈希表将包含所有语言和键以及将其视为值的人员列表。这是您想要的数据,但它不是您想要的格式。事实上,如果我们把它放在finally
块
(return hash)
我们会得到一些半有用的输出*,告诉我们我们正在走上正轨。
#S(HASH-TABLE :TEST FASTHASH-EQL ((TOM GERMAN FRENCH) . (TOM TOM))
((AMANDA ITALIAN SPANISH ENGLISH) . (AMANDA AMANDA AMANDA))
((DAVID SPANISH GERMAN) . (DAVID DAVID)))
相反,让我们再做一个循环,将此哈希表转换为您希望它的列表。这就是我们现在想要的finally
块。
(return (loop for key being the hash-keys of hash using (hash-value value)
collect (list key value)))
这对being
宏使用相对模糊的loop
语法,允许在哈希表上轻松迭代。您应将其读作:对于每个键值对,收集包含键的列表,然后将值放入列表中,然后返回累积列表。这是loop
宏的另一个有趣特性:它试图为常见用例提供原语,例如将值累积到列表中。在这种情况下它会派上用场。
这是完整的代码块。
(defun lenguages (names)
(loop with hash = (make-hash-table)
for entry in names
for name = (car entry)
do (loop for lang in (cdr entry)
do (push name (gethash lang hash)))
finally (return (loop for key being the hash-keys of hash using (hash-value value)
collect (list key value)))))
我之前提供的链接是关于Common Lisp的GigaMonkeys书籍,available online for free。我强烈鼓励通读它,因为它是所有Common Lisp的惊人参考。特别是如果你刚刚开始,那本书真的可以让你朝着正确的方向前进。
*您的输出格式可能因此而异。实现选择如何输出结构。
答案 1 :(得分:1)
另一个答案很好:这是一个不使用loop
或中间哈希表的版本,而是直接构建所需的关联列表。值得将它的效率与基于散列表的效率进行比较:它在搜索列表中做了更多的搜索,但实际上,对于少量数据,这些事情通常更快(哈希表在很多方面都有很大的开销)实现)并且它总是使用更少的存储,因为它不构建它不返回的结构。
请注意:
(languages '((david german german)))
为((german (david)))
而非((german (david david)))
- 它会支付一些性能成本(对于大数据可能会有所改善)这样做会使用更多哈希表。所以,这是:
(defun languages (people)
(let ((langs '())) ;the map we are building
(dolist (pl people langs)
(destructuring-bind (person . person-languages) pl
(dolist (lang person-languages)
(let ((entry (assoc lang langs)))
(if (not (null entry))
;; there's an entry for lang: add the person to it
(pushnew person (second entry))
;; there is no entry, create one with person in it
(setf langs `((,lang (,person)) ,@langs)))))))))
(另请注意,基于loop
的版本可以使用loop
的解构,这可能会更清晰一点。)