收集向量而不是列表

时间:2017-05-28 07:18:33

标签: vector iteration common-lisp

我使用SBCL和来自quicklisp的iterate包解决了Project Euler的第8个问题。在我的代码中,我定义了一个函数,将一个数字转换为它的数字列表。这是源代码:

(defun number-to-list (n)
  (iter (for c in-string (write-to-string n)) (collect (digit-char-p c))))

collectiter中的loop子句会列出值列表。是否有可能生成一个向量(一维数组)?

我唯一的选择是将number-to-list生成的列表转换为向量吗?因为这似乎效率低下(虽然可能 效率低下)

3 个答案:

答案 0 :(得分:2)

通常有一个大问题:结果向量有多大?最好先知道,然后我们可以用正确的大小分配矢量一次。否则我们会找到解决这个问题的方法:使用可调整大小的向量,先分配一个列表并稍后复制到结果向量中,使用填充指针分配一个更大的向量,...

如果你有一个序列,那么可以使用Common Lisp函数MAP:如果源对象是一个向量,这里是一个字符串,它的长度很便宜。

CL-USER 1 > (map 'vector
                 #'digit-char-p
                 (write-to-string 5837457324534))
#(5 8 3 7 4 5 7 3 2 4 5 3 4)

您可以使用ITERATE并收集矢量:

FOO 32 > (defun number-to-vector (n)
           (iter (for c in-string (write-to-string n))
             (collect (digit-char-p c) result-type vector)))
NUMBER-TO-VECTOR

FOO 33 > (number-to-vector 8573475934)
#(8 5 7 3 4 7 5 9 3 4)

如果查看宏扩展,它实际上会收集到一个列表中,然后调用COERCE来创建向量。所以:没有效率的胜利。

请注意,这是ITERATELOOP更强大的另一个示例:标准LOOP无法直接从 collect 返回向量。

答案 1 :(得分:2)

建议的解决方案是正确和优雅的,但它们首先创建一个列表,或者用字符串转换数字。我想建议从整数到数组的直接转换,而不首先转换列表或字符串中的数字:

JobDispatcher

使用给出整数n的十进制数字的公式来解决分配正确维度的数组的问题:⌊log 10 n⌋+ 1。

答案 2 :(得分:1)

也许不是你问题的直接答案,但这里是我经常使用的num-to-list和list-to-num函数。

(defun num-to-list-helper (n liste)
  (cond ((< n 1) liste)
    (t (num-to-list-helper (truncate (/ n 10)) (cons (rem n 10) liste))))))

(defun num-to-list (n)
  (num-to-list-helper n nil))

(defun list-to-num-helper (liste n)
  (if (null liste)
      n
      (list-to-num-helper (cdr liste)
              (+ n (* (car liste) (expt 10 (1- (length liste))))))))
(defun list-to-num (liste)
  (list-to-num-helper liste 0))

您可以尝试这些,看看是否有将数字转换为字符串的改进。就个人而言,我不喜欢数字字符串,因为我认为它们是我在Java时代被迫做的一个丑陋的技巧。

您还可以使用向量将这些函数转换为版本,并查看它们的用途。