根据 Lisp 中的字符匹配对列表进行排序

Sort list based on character matching in Lisp

根据拼写中的常用字符计算并return生成候选列表。

例如, 如果列表是:(团队比他们更年轻) 你在函数中提供参数 ("thim") 然后它应该根据列表中常见字符的相似性对列表进行排序。 它应该 return:(他们的时间团队比城市青少年) 由于 THEM 与 "thim" 具有更常见的字符,因此它排在前面,依此类推。

我的尝试:

       (defun correctSX_SIM(word)

        (setf w (correctSX word))  ; w is list of words.
        (sort w #'eq :key #'car)

       )

我知道我的回答有偏差。但是我需要 LISP 的帮助,因为我不知道 LISP 的所有内置功能。

首先,为已知单词列表定义一个特殊变量:

(defparameter *dictionary* '(TEAM TEEN THAN THEM THEN TIME TOWN))

您必须为满足您要求的单词定义一个距离函数。如果我没记错的话,以下应该有效:

(defun distance (u v)
  (- (length (intersection (coerce u 'list)
                           (coerce v 'list)))))

我们查看两个字符串中共同元素的数量并将其取反,因此共享元素数量最多的元素得分最低。我不知道这是否重要,但相同的长字符串比相同的短字符串的得分低。

根据您的要求,您需要进行稳定排序,使与所选单词距离相同的单词保持相对顺序。这也是我使用严格的 #'< 比较函数的原因:当比较两个词 ab 与输入的距离相等时, returns nil a < bb < a 的比较,让 STABLE-SORT 知道ab 是等价的 w.r.t。偏序。 另请注意,我使用 COPY-LIST 来避免改变字典。对于您的示例,实际排序如下:

(stable-sort (copy-list *dictionary*) 
             #'< 
             :key (lambda (e) (distance "THIM" (string e))))

=> (them time team than then teen town)

结果与你的例子略有不同,但我认为它符合THAN应该在THEN[=58之前=] 因为 (i) 在这种情况下距离相同,并且 (ii) 它们在原始列表中按该顺序出现。

正如@jkiiski 在评论中指出的那样,每次比较可能会调用一次距离函数(即 O(n.log(n)))。在我看来,在那种特殊情况下,距离函数非常便宜,但是对于大型数据集和更复杂的距离,缓存中间结果肯定会有所回报。您至少有两个选择:

  • 定义另一个缓存已知结果的距离函数 (memoization)。这种方法的优点是您在排序部分和距离函数之间保持严格的分离。这应该足够了:

    (ql:quickload :memoize)
    (org.tfeb.hax.memoize:memoize-function 'distance
                                           :key #'identity
                                           :test #'equal)
    
  • 将距离预先计算到另一个包含 (distance string) 对的列表中,并根据第一个元素排序。然后,提取所有第二个元素以获得一个字符串序列。显然这被称为 Schwartzian transform (感谢 Svante)。这里整个过程更明确一点:

    (defun sorted-dictionary (input-word)
      (let ((list
              (stable-sort
                (loop for word in *dictionary*
                      collect (cons 
                                (distance input-word (string word))
                                word))
                 #'<
                 :key #'car)))
        (map-into list #'cdr list)))
    

但是有一个主要区别:使用 memoized 版本,您可以在不同的排序调用中保留已知结果(除非您明确清除它们),而使用 sorted-dictionary,一旦结果列表被计算出来,你就丢弃中间距离。

有个Levenshtein distance algorithm for measuring the difference between two words. Here you can find complete implementation for Common Lisp. Also, the easier way to show a difference between two strings is set-difference function as shown here.