2013-04-02 2 views
-1

Я относительно новым сюсюкать и любопытно, как на лучший способ для работы с вложенным списком в следующем контексте:Работы с вложенными списками LISP

Итак, у меня есть следующие функции:

(defun get-p0 (points) 
    (loop for (label x y) in points 
    ; collect (list (if (> x y) (+ 2 3)))) 
    collect (list (get-angle (first points) (second points)))) 
) 

Я называю это так:

(get-p0 '((A 5 2) (B 2 3) (C 8 9))) 

То, что я пытаюсь сделать, это получить угол каждой координаты относительно других координат. Например, угол AB, AC, BA, BC, CA, CB и распечатать их. Выход, который я получал, выглядит следующим образом:

((161.56505) (161.56505) (161.56505)) 

Это было действительно просто для целей тестирования в этот момент. Хотя то, что я действительно хотел бы сделать, это вывести самую низкую и самую левую координату. Есть идеи?

ответ

2

Я сделал очень похожее упражнение некоторое время назад. Это похоже, что это может быть полезным для вас:

;; Define struct `point' 
(defstruct point x y) 

;; Define methods specializing on `point' 
(defgeneric add (a b)) 

(defgeneric subtract (a b)) 

(defgeneric distance (a b)) 

(defgeneric projection (a)) 

(defmethod add ((this point) (that point)) 
    (make-point :x (max (point-x this) (point-x that)) 
       :y (max (point-y this) (point-y that)))) 

(defmethod subtract ((this point) (that point)) 
    (make-point :x (min (point-x this) (point-x that)) 
       :y (min (point-y this) (point-y that)))) 

(defmethod distance ((this point) (that point)) 
    (let ((a (add this that)) (b (subtract this that))) 
    (make-point :x (- (point-x a) (point-x b)) 
       :y (- (point-y a) (point-y b))))) 

(defmethod projection ((this point)) 
    (sqrt (+ (expt (point-x this) 2) (expt (point-y this) 2)))) 

;; Define helper functions 
(defun angle (a b c) 
    (acos (/ (+ (* a a) (* b b) (- (* c c))) (* 2 a b)))) 

(defun radian->degree (radian) (/ (* 180 radian) pi)) 

;; Define struct `triangle' 
(defstruct triangle 
    (a nil :type (or null point)) 
    (b nil :type (or null point)) 
    (c nil :type (or null point))) 

;; Define methods specializing on `triangle' 
(defgeneric angles-of (triangle)) 

(defgeneric sides-of (triangle)) 

(defgeneric points-of (triangle)) 

(defmethod points-of ((this triangle)) 
    (let ((result (list (triangle-a this) (triangle-b this) (triangle-c this)))) 
    (nconc result result))) 

(defmethod sides-of ((this triangle)) 
    (loop for (p . rest) on (points-of this) 
    for i from 0 below 3 
    collect (projection (distance p (car rest))) into result 
    finally (return (nconc result result)))) 

(defmethod angles-of ((this triangle)) 
    (loop for (a b c) on (sides-of this) 
     for i from 0 below 3 
     collect (radian->degree (angle a b c)) into result 
     finally (return (nconc result result)))) 

;; Create some test triangle 
(defvar *pythagorean-triangle* 
    (make-triangle :a (make-point :x 1 :y 2) 
       :b (make-point :x 4 :y 2) 
       :c (make-point :x 4 :y 6))) 

;; Finally! don't forget to 
(setf *print-circle* t) 
;; so you can see circular lists' content 
(angles-of *pythagorean-triangle*) 

#1=(90.00000265626015d0 36.86989784081561d0 53.13009995842113d0 . #1#) 

Несколько заметок, я увидел в другом посте была некоторая путаница в форме

(loop for <list-like expression> in some-list ...) 

Это list-like expression является то, что обычно называют «уничтожение того, связываются ». Это ограниченное средство сопоставления шаблонов. Эффективно это шаблон, который отображает символы на вас, определенные внутри шаблона, на любые значения, найденные в списке, который вы повторяете.

Так, например, (loop for (x y) on '(1 2 3 4)) будет связывать x и y к 1 и 2, то 2, 3, затем 3, 4 и, наконец, 4nil. Конечно, вы можете использовать больше переменных/вы можете использовать пунктирный список для шаблона и т. Д.

1

Вы правильно привязываете значения к каждой итерации с помощью формы for (x y z) in list. Но когда вы делаете сбор в следующей строке, вы берете значения с начала list. Это значение никогда не изменится при оценке цикла.

Вы должны изменить свой код, чтобы выглядеть следующим образом:

(defun get-p0 (points) 
    (loop 
     for (label x y) in points 
     collect (list (get-angle x y)))) 
1

Если я правильно понимаю вашу цель правильно, вы хотите, чтобы потреблять список как '((A 5 2) (B 2 3) (C 8 9)) и вернуть что-то вроде (2 2) (точка А имеет y 2 и точка B имеет x из 2, и это самые низкие координаты). Я хотел бы сделать что-то вроде

(loop for (_l x y) in lst 
     minimizing y into min-y minimizing x into min-x 
     finally (return (list min-x min-y))) 

loop глубокая и мощная конструкция, поэтому я рекомендую вам прочитать relevant PCL chapter и loop spec (это очень легко забыть директиву, которая может оказаться идеальным для данная ситуация). Если вы новичок в Lisp, вы можете взглянуть на the entire book.

+0

@wvxvw - Yup; эти конкретные ключевые слова избежали меня в то время. Измененный. – Inaimathi