代码之家  ›  专栏  ›  技术社区  ›  Leandro Zanarkand

显示列表

  •  0
  • Leandro Zanarkand  · 技术社区  · 9 年前

    我有一个名字和不同语言的列表

    (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))

    2 回复  |  直到 9 年前
        1
  •  3
  •   Silvio Mayolo    9 年前

    loop 宏。您可以在中阅读有关此宏的所有详细信息 the GigaMonkeys book ,但我们将在这里讨论解决此问题所需的部分。让我们从函数定义开始。

    (defun lenguages (names)
      ...)
    

    在本文中,我们希望迭代提供的列表。我们还想收集一些密钥,因此哈希表很有用。哈希表(在许多其他语言中称为映射或dict)以高效的方式将键与值关联。

    (loop with hash = (make-hash-table)
          for entry in names
          for name = (car entry)
          do ...
          finally ...)
    

    环 宏非常强大,有自己的语言。这个 with for 定义迭代变量。循环将以 entry 绑定到的每个条目 names 当条目用完时将停止。第三行是另一个局部变量,但与 对于 变量每次都会反弹,所以每次迭代时 name 将是 do 块包含将在每次迭代中执行的任意Lisp代码,以及 finally 包含要在循环结束时执行的Lisp代码块。

    做 块,我们想将该人的名字添加到他们知道的每种语言的哈希表条目中,因此我们需要另一个 环

    (loop for lang in (cdr entry)
          do (push name (gethash lang hash)))
    

    这个循环进入 做 nil 如果哈希键不存在,则将元素前置到 无

    现在,当这个循环完成时,哈希表将包含所有语言和键,以及知道它们是值的人的列表。这是您想要的数据,但不是您想要的格式。事实上,如果我们把这个放在 最后 块

    (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)))
    

    相反,让我们再进行一次循环,将此哈希表转换为您想要的列表。这是我们想要的 最后 立即阻止。

    (return (loop for key being the hash-keys of hash using (hash-value value)
                  collect (list key value)))
    

    这使用了相对模糊的 being 的语法 环 环 宏有趣的特性:它试图为常见用例提供原语,例如将值累积到列表中。在这种情况下它很有用。

    这是完整的代码块。

    (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)))))
    

    available online for free


        2
  •  1
  •   user5920214 user5920214    9 年前

    另一个答案很好:这是一个不使用 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)))))))))
    

    (另请注意 环 环 ,这可能会更清楚一些。)