如何用类似Lisp的语言分解此过程?
在整个过程中,有什么方法可以分解功能:制作一个对象的副本,将其副本排列在另一个对象的边框上,设置其副本的旋转度?
最后一步特别有趣,它也介绍了如何与以前的步骤组合。
答案 0 :(得分:7)
您的问题很广泛,让我们举一个简单的2D形状示例。
下面的代码定义为我们可以编写以下代码:
(isomorphism (alexandria:compose (scale 10)
(lift #'round)
(rotate 90))
(triangle (point 0 0)
(point 1 0)
(point 0 1)))
=> (TRIANGLE (POINT 0 0) (POINT 0 10) (POINT -10 0))
这将计算一个简单的变换函数,称为同构(保留形状),首先是旋转,然后是对计算点的舍入,然后是缩放操作。结果是描述结果形状的列表。复制形状只是具有功能#'identity
的同构(但是如果使用纯函数方式,则有点用)。
NB:四舍五入例如当cos / sin产生非常小的浮点时回落到零;使用浮点会破坏形状的保留,并且舍入也会破坏圆角,但是在此处将它们组合在一起时,得到的形状是实际的同构。根据您的需求的目的,这种正确性/准确性可能不重要。您还可以描述应用了哪些转换,并且仅“栅格化”它们以进行显示。
变换函数作用于坐标列表,并返回坐标列表:
(defun translate (dx &optional (dy dx))
(lambda (xy) (mapcar #'+ xy (list dx dy))))
(defun scale (sx &optional (sy sx))
(lambda (xy) (mapcar #'* xy (list sx sy))))
(defun rotate (degrees)
(let* ((radians (* degrees pi 1/180))
(cos (cos radians))
(sin (sin radians)))
(lambda (xy)
(destructuring-bind (x y) xy
(list (- (* x cos) (* y sin))
(+ (* y cos) (* x sin)))))))
(defun lift (fn)
(lambda (things)
(mapcar fn things)))
isomorphism
函数的定义如下,并递归地将形状分解为类型标签(可作为构造函数加倍使用)和组件,并在出现点的情况下应用变换函数:
(defun isomorphism (transform shape)
(flet ((isomorphism (s) (isomorphism transform s)))
(with-shape (constructor components) shape
(apply constructor
(if (eq constructor 'point)
(funcall transform components)
(mapcar #'isomorphism components))))))
我对shape
和with-shape
的定义如下,以便对它们的表示方式有一点抽象:
(defun shape (constructor components)
(list* constructor components))
(defmacro with-shape ((constructor components) shape &body body)
`(destructuring-bind (,constructor &rest ,components) ,shape
,@body))
我可以用简单的函数定义形状,这些形状可能会或可能不会执行某些检查和归一化:
(defun point (&rest coords)
(shape 'point coords))
(defun triangle (a b c)
(shape 'triangle (list a b c)))
(defun rectangle (x0 y0 x1 y1)
(shape 'rectangle
(list (min x0 x1)
(min y0 y1)
(max x0 x1)
(max y0 y1))))
请注意,构造函数始终与函数名称相同。可以使用宏来强制执行此操作,您只需要返回组件列表即可:
(defconstructor point (x y)
(list x y))
您还可以从上面的一个构建派生构造函数:
(defun rectangle-xywh (x y width height)
(rectangle x y (+ x width) (+ y height)))
以上形状是根据点定义的,但是您可以想象将形状由较小的形状组装而成:
(defun group (&rest shapes)
(shape 'group shapes))
这只是一个玩具示例,但作为起点可能有用。
然后,如果要制作形状并以90°的增量旋转不同的副本,则可以执行以下操作:
(loop
for angle from 0 below 360 by 90
collect
(isomorphism (compose (lift #'round)
(rotate angle)
(scale 2)
(translate 10 0))
(group (triangle (point 0 0)
(point 1 0)
(point 0 1)))))