根据 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)))))
我们查看两个字符串中共同元素的数量并将其取反,因此共享元素数量最多的元素得分最低。我不知道这是否重要,但相同的长字符串比相同的短字符串的得分低。
根据您的要求,您需要进行稳定排序,使与所选单词距离相同的单词保持相对顺序。这也是我使用严格的 #'<
比较函数的原因:当比较两个词 a 和 b 与输入的距离相等时, returns nil
a < b 和 b < a 的比较,让 STABLE-SORT
知道a 和 b 是等价的 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.
根据拼写中的常用字符计算并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)))))
我们查看两个字符串中共同元素的数量并将其取反,因此共享元素数量最多的元素得分最低。我不知道这是否重要,但相同的长字符串比相同的短字符串的得分低。
根据您的要求,您需要进行稳定排序,使与所选单词距离相同的单词保持相对顺序。这也是我使用严格的 #'<
比较函数的原因:当比较两个词 a 和 b 与输入的距离相等时, returns nil
a < b 和 b < a 的比较,让 STABLE-SORT
知道a 和 b 是等价的 w.r.t。偏序。
另请注意,我使用 COPY-LIST
来避免改变字典。对于您的示例,实际排序如下:
(stable-sort (copy-list *dictionary*)
#'<
:key (lambda (e) (distance "THIM" (string e))))
=> (them time team than then teen town)
结果与你的例子略有不同,但我认为它符合
正如@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.