需要有关如何在lisp中打印矩阵的建议

时间:2013-09-21 04:23:29

标签: opencv printing lisp common-lisp cffi

我有一个矩阵定义,所以如果我这样做

(format t "~a" (get-real-2d 0 0))

它打印出第一行第一列中的元素

如果我这样做

(format t "~a" (get-real-2d a 0 1))

它打印出第一行第二列中的元素

如果我这样做

(format t "~a" (get-real-2d a 1 0))

它打印出第二行第一列中的元素。

矩阵a看起来像这样

a =
((0 1 2)
(3 4 5)
(6 7 8))

我希望你能告诉我如何编写dotimes循环或其他循环 尽可能少的行将使用get-real-2d函数打印出矩阵,因此输出如下所示:

0 1 2 
3 4 5
6 7 8

我只是希望你能给我看一个非常小的光滑循环,我可以用来打印矩阵,我可以在我的lisp库中使用一些真正专业的东西,比如只使用变量的矩阵。类似的东西:

(format t "~a" (get-real-2d i j))

而不是一堆:

(format t "~a" (get-real-2d 0 0))
(format t "~a" (get-real-2d 0 1))
(format t "~a" (get-real-2d 0 2))

;;;;最新编辑;;; 为了使这个简单,我打电话

(defparameter a (create-mat 3 3 +32fc1+))

创建一个3x3矩阵 - create-mat是opencv的cvCreateMat的包装器

repl上该命令的输出是

(defparameter a (create-mat 3 3 +32fc1+))
A
CL-OPENCV> a
#.(SB-SYS:INT-SAP #X7FFFD8000E00)

i / e变量a是指向3x3矩阵的指针

然后我跑

(defparameter data (cffi:foreign-alloc :float :initial-contents 
          '(0.0f0 1.0f0 2.0f0 3.0f0 4.0f0 5.0f0 6.0f0 7.0f0 8.0f0)))

为矩阵创建数据 - 我接下来将分配给矩阵

repl上该命令的输出是

CL-OPENCV> (defparameter data (cffi:foreign-alloc :float :initial-contents 
          '(0.0f0 1.0f0 2.0f0 3.0f0 4.0f0 5.0f0 6.0f0 7.0f0 8.0f0)))
DATA
CL-OPENCV> data
#.(SB-SYS:INT-SAP #X7FFFD8000E40)

i / e变量a是指向数据插入矩阵的数据指针

然后我打电话给..

(set-data a data 12) to add the data to the matrix - set-data is a wrapper for opencv's cvSetData

所以现在当我运行 - (get-real-2d是opencv的cvGetReal2d的包装器)

(get-real-2d a 0 0)  it gets the element of matrix a at row 0 col 0 which is 0.0d0

repl上该命令的输出是

CL-OPENCV> (get-real-2d a 0 0)
0.0d0

现在我跑的时候

(get-real-2d a 0 1)  it gets the element of matrix a at row 0 col 1 which is is 0.0d0

repl上该命令的输出是

CL-OPENCV> (get-real-2d a 0 1)
1.0d0

当我运行此循环时

 (dotimes (i 3)
  (dotimes (j 3)
(format t "~a~%" (get-real-2d a i j))))

repl上该命令的输出是

CL-OPENCV> (dotimes (i 3)
  (dotimes (j 3)
(format t "~a~%" (get-real-2d a i j))))
0.0d0
1.0d0
2.0d0
3.0d0
4.0d0
5.0d0
6.0d0
7.0d0
8.0d0
NIL

但是当我尝试你的方法@Svante

(dotimes (i 3)
  (dotimes (j 3)
(format t "~{~{~a~^ ~}~%~}" (get-real-2d a i j))))

我收到错误:

The value 0.0d0 is not of type LIST.
    [Condition of type TYPE-ERROR]

因为get-real-2d的1次运行的输出只是1次浮点数i / e

CL-OPENCV> (get-real-2d a 0 0)
0.0d0

有了这些信息,你可以帮我打印矩阵,看起来像这样吗

0.0d0 1.0d0 2.0d0 
3.0d0 4.0d0 5.0d0
6.0d0 7.0d0 8.0d0

4 个答案:

答案 0 :(得分:5)

您可以直接在format指令中执行此操作。格式说明~{~}会进入列表结构。

(format t "~{~{~a~^ ~}~%~}" matrix)

外部~{ ~}对遍历矩阵的第一级,因此内部的指令一次只能看到一行。内部~{ ~}对遍历每一行,以便内部的指令一次看到一个元素。 ~A打印该元素。 ~^~}之间的部分仅在循环体的执行之间打印,而不是在结尾处执行。 ~%会发出#\Newline

按要求编辑

请注意,~{ ~}会替换循环,并且我将变量命名为matrix,而不是element。你需要将整个矩阵放在那里,它应该是嵌套列表的形式。我从您a ((0 1 2) (3 4 5) (6 7 8)) (format t "~{~{~a~^ ~}~%~}" a)的陈述中推断出这一点。所以,dotimes

如果矩阵不是以嵌套列表的形式而是某种类型的数组,那么你真的需要遍历索引。首先,嵌套的(fresh-line) (dotimes (i (array-dimension array 0)) (dotimes (j (array-dimension array 1)) (format t "~a " (aref array i j))) (terpri)) 表单应该足够了:

array-dimension

我不知道您的矩阵如何映射到数组,因此您必须将aref和{{1}}替换为您的版本。

答案 1 :(得分:1)

这并不难,所以我宁愿把它留给你弄清楚,但这里有一些提示,以制作一个“光滑的循环”Lisp风格。我会建议mapc(或mapcar)的一个或多个实例,而不是dotimes。如果你不习惯函数式编程,这可能会有点奇怪,但是一旦你习惯了它,它比dotimes更容易阅读,你不必跟踪索引,所以它可以避免错误。如果你还不熟悉它们,你真的应该学会使用mapcar/mapc。他们很酷。或者,如果你想真的很酷:-)你可以使用递归迭代矩阵,但我认为为此目的,使用mapc进行迭代将更容易阅读。 (但是你应该学习其他工作的递归方式。如果你发现递归令人困惑 - 我没有理由认为你这么做,但是有些人有麻烦 - 我最喜欢的教程是 The Little Schemer 。)

您可能还想使用其他format指令,如果数字没有足够的数字,则允许您使用空格填充数字。 ~%指令也可能有用。彼得塞贝尔有一个非常好的introduction to format

答案 2 :(得分:1)

您的问题可以通过两种方式理解,这就是为什么它有两个解决方案:

  • 定义打印矩阵类型对象的方法(在这种情况下,它可以使用有关矩阵内部结构的知识):

    (defmethod print-object ((matrix matrix) stream)
        (format stream "~{~{~a~^ ~}~%~}" matrix))
    

    使用format,如答案中所示。

  • 定义可以使用对象的唯一方法的客户端函数 - get-real-2d

    (defun print-matrix (matrix dimension-x dimension-y)
        (dotimes (x dimension-x)
            (dotimes (y dimension-y)
                (princ (get-real-2d matrix x y))
                (princ #\Space))
            (princ #\Newline)))
    

    只需使用dotimes

答案 3 :(得分:1)

以下是您要求的两个dotimes循环。您需要注意的唯一事项是何时打印空格以及何时打印换行符。

(dotimes (i 3)
  (dotimes (j 3)
    (princ (get-real-2d a i j))
    (if (< j 2)
      (princ #\Space)
      (terpri))))

或者,您可能希望使用format指令进行浮点打印,以使数字始终在漂亮的列中对齐。您可以选择永不打印指数的~F,始终打印一个指数的~E,以及根据幅度行事的~G。在HyperSpec中查找详细信息:http://www.lispworks.com/documentation/HyperSpec/Body/22_cc.htm

以下是使用~F的示例,其最大字段宽度为5和1个小数位:

(dotimes (i 3)
  (dotimes (j 3)
    (format t "~5,1F" (get-real-2d a i j)))
  (terpri))