Vector 收集到向量而不是列表

Vector 收集到向量而不是列表,vector,iteration,common-lisp,Vector,Iteration,Common Lisp,我使用SBCL和quicklisp中的iterate包解决了ProjectEuler的第8个问题。在我的代码中,我定义了一个函数,该函数将一个数字转换为它的数字列表。以下是源代码: (defun number-to-list (n) (iter (for c in-string (write-to-string n)) (collect (digit-char-p c)))) iter和loop中的collect子句将这些值列成一个列表。是否可以生成向量(一维数组) 我唯一的选择是将num

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

(defun number-to-list (n)
  (iter (for c in-string (write-to-string n)) (collect (digit-char-p c))))
iter
loop
中的
collect
子句将这些值列成一个列表。是否可以生成向量(一维数组)


我唯一的选择是将
number生成的列表转换成list
向量吗?因为这看起来效率很低(虽然可能没有那么低)

可能不是你问题的直接答案,但这里是我经常使用的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))
(将num定义为list helper(n liste)
(cond((
您可以试试这些,看看将数字转换为字符串是否有改进。就我个人而言,我不喜欢数字串,因为我把它们看作是我java时代被迫做的一个丑陋的把戏。
您还可以将这些函数转换为使用向量的版本,并查看它们是如何工作的。

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

如果您有一个序列,那么可以使用公共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)
您可以使用
迭代
并收集向量:

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)
如果您查看宏展开,它实际上会收集到一个列表中,然后调用
强制
来创建向量。所以:效率没有赢家


注意,这是另一个例子,
迭代
循环
更强大:标准的
循环
不能直接从collect返回向量。

建议的解决方案是正确和优雅的,但它们首先创建一个列表,或将数字转换为字符串。我想提出一种从整数到数组的直接转换,无需首先转换列表或字符串中的数字:

(defun digits(n)
  "Transform a positive integer n in array of digits"
  (let* ((logn (floor (log n 10)))
         (result (make-array (1+ logn) :element-type '(integer 0 9))))
    (loop for i downfrom logn to 0
       do (setf (values n (aref result i)) (floor n 10)))
    result))

分配正确维数的数组的问题通过给出整数n的小数位数的公式来解决:⌊log10n⌋+1.

谢谢,我想我会使用功能性更强的方法,因为我在CL中使用Project Euler的全部原因是为了避免难看的代码(我在C中遇到了一些问题,我叔叔帮我优化了它们,考虑到所有的位和字节让我发疯),现在我有了一个漂亮且性能良好的(它应该运行得很好吧?)解决方案我有两个小问题(我会把它们分成两条评论):我想做一个项目。我知道quickproject,但我无法找出表示此项目的最佳方式。我应该为每个问题定义一个包吗?1个解决所有问题的包?在不同的lisp文件中拆分所有内容,而不是将所有解决方案放在一个大的lisp文件中?如果是这样的话,在编译时我如何将文件“链接”在一起?我想将其编译为Windows可执行文件(将使用Linux,但我只有我爸爸的计算机)。我该怎么做?(我知道如何将单个文件编译为fasl和exe,但如何编译整个项目?)因此,要从多个文件生成可执行文件,我只需将它们全部加载到当前的lisp实例中,然后
保存lisp并死亡
?很酷,我会考虑的。谢谢分享你的功能,但是我想有一些简单的代码我可以理解,而不是仅仅从互联网上复制一个“神奇的解决方案”,而且这个代码在不贯穿整个过程(心理上或调试器)的情况下不是特别容易理解的。@omrisim210尝试
跟踪
帮助器函数。您将看到每个参数在过程中的每一步都是如何变化的。真正的工作是由“helper”函数完成的。它们是递归的。他们接受一个参数,处理它,在第二个参数中收集结果,然后用第一个参数的更短或更小版本来称呼自己。当第一个参数太小或为null(取决于它是数字还是列表)时,它们停止
trace
是你的朋友。我很快就会这么做,现在我休息一下,因为我的大脑因为解决问题而受伤:)谢谢你的解决方案,看起来它会非常有效,但CL就像我从C中的“逃避语言”(我必须使用所有这些数学技巧对数字进行运算,如果必须使用字符串,我需要动态分配它们)。将数字转换为字符串并将字符串转换为向量对我来说就足够了。请注意,
LOG
需要非零数字,因此
(数字0)
应该单独处理。@jkiiski,你是对的,因此我在注释字符串中指定该函数仅用于正整数。你不需要
多值绑定
(setf(values n(aref result I))(floor n 10))