【问题标题】:What would be a good or efficient way to get the list of alphabet used in a string什么是获取字符串中使用的字母列表的好方法或有效方法
【发布时间】:2012-12-20 03:17:27
【问题描述】:

简单来说,Common Lisp中如何从字符串中获取不重复的字母列表?

例如:

"common"
-> ("c" "o" "m" "n") or in characters, (#\c #\o #\m #\n)
I'd care less about the order and type, if it is in string or character.

"overflow" -> (o v e r f l w)
"tomtomtom" -> (t o m)
   etc...

我的想法是收集原始字符串的第一个字母, 然后使用函数;

(remove letter string)

收集现在的第一个字母删除的字母串并将其附加到之前已经收集的字母中。 这听起来像递归,但如果递归调用会丢失之前收集的 *letter*s 列表,对吗?我也怀疑是否有任何内置函数。

此外,我不想使用 set 或其中任何一个,因为我想要 完全以实用的方式完成此操作。

感谢您的宝贵时间。

【问题讨论】:

    标签: string common-lisp


    【解决方案1】:

    这里是一些 Haskell 中的代码,因为我对 Lisp 不太熟悉,但由于它们都是函数式的,我认为翻译它不会有问题:

    doit :: String -> String
    
    doit [] = []
    doit (x:xs) = [x] ++ doit (filter (\y -> x /= y) xs)
    

    那它有什么作用呢?你有一个字符串,如果它是一个空字符串(在 Haskell [] == "" 中),你返回一个 empty 字符串。 否则,取第一个 element 并将其连接到 String 的 tail 上的递归,但 filter 去掉那些元素,它们是 == first element

    此函数filter 只是特定map-function 的语法糖,在Lisp 中称为remove-if,您可以在此处重新阅读:lisp filter out results from list not matching predicate

    【讨论】:

    • @wvxvw 是什么让您认为它具有二次内存使用率?
    • @wvxvw 实际上,由于懒惰,它只会创建一个列表(通过所有先前创建的过滤器的元素)。但即使有急切的评估,一次也只会有一个有效的列表,因此内存使用量将是线性的[当然,总分配量将是二次方的,除非有足够的重复次数]。
    • @wvxvw 任何时候都需要的最大值。尽管总分配有时也是一个有趣的衡量标准,但这并不是内存复杂性的含义。是的,对于手头的问题,这不是一个好的算法。然而,它在内存使用方面并不是特别糟糕(尽管也可以改善这一点)。
    【解决方案2】:
    CL-USER> (remove-duplicates (coerce "common" 'list))
    (#\c #\m #\o #\n)
    

    或者你甚至可以简单地这样做:

    CL-USER> (remove-duplicates "common")
    "comn"
    

    【讨论】:

    • 哇,谷歌搜索了很多,但从来没有遇到过remove-duplicates 函数。无论如何,谢谢
    【解决方案3】:

    如果您可以对正在处理的文本做出一些假设,那么可能会有更好的可能性来做到这一点。例如,如果您只处理英文文本,那么您可以实现一个非常简单的哈希函数(基本上,使用 128 个元素长的位向量),这样您甚至不需要使用一个哈希表(这是一个更复杂的结构)。下面的代码说明了这个想法。

    (defun string-alphabet (input)
      (loop with cache =
           (coerce (make-array 128
                               :element-type 'bit
                               :initial-element 0) 'bit-vector)
         with result = (list input)
         with head = result
         for char across input
         for code = (char-code char) do
           (when (= (aref cache code) 0)
             (setf (aref cache code) 1
                   (cdr head) (list char)
                   head (cdr head)))
         finally (return (cdr result))))
    
    (string-alphabet "overflow")
    ;; (#\o #\v #\e #\r #\f #\l #\w)
    

    强制转换为bit-vector 并不是很重要,但它更容易调试(打印的形式更紧凑),并且某些实现实际上可能会优化它以仅包含平台需要表示的这么多位的整数,即在128位长度的情况下,在64位平台上,它可以短至2或3个整数。

    或者,您也可以这样做,使用整数:

    (defun string-alphabet (input)
      (loop with cache = (ash 1 128)
         with result = (list input)
         with head = result
         for char across input
         for code = (char-code char) do
           (unless (logbitp code cache)
             (setf cache (logior cache (ash 1 code))
                   (cdr head) (list char)
                   head (cdr head)))
         finally (return (cdr result))))
    

    但在这种情况下,在最坏的情况下,您会创建 128 个大整数,这毕竟不是那么昂贵,但位向量可能会做得更好。但是,这可能会给您一个提示,对于这种情况,您可以假设,例如,只使用英文字母(在这种情况下,可以使用比机器记忆词更短的整数)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-03
      • 2016-11-03
      • 1970-01-01
      • 2011-02-10
      • 2014-12-08
      • 2011-05-29
      相关资源
      最近更新 更多