Как использовать только одну функцию перемещения для всех фигур - PullRequest
0 голосов
/ 17 октября 2018

У меня проблема с функцией move в моем коде.Мне нужно, чтобы это было:

  1. одна функция, которая может перемещать все фигуры, или
  2. несколько функций с одинаковым именем.

Пока у меня есть move функции с разными именами для точки, круга и многоугольника .Я не могу понять, как заставить переместить функцию для изображения.

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

    ;
    ; POINT
    ;

    (defun make-point ()
      (list (list 0 0) :black))

    (defun x (point)
     (caar point))

    (defun y (point)
      (cadar point))

    (defun set-x (point new-x)
      (setf (caar point) new-x)
      point)

    (defun set-y (point new-y)
      (setf (cadar point) new-y)
      point)

    (defun move (point dx dy)
     (set-x point (+ (x point) dx))
     (set-y point (+ (y point) dy))
     point)

    ;
    ; CIRCLE
    ;

    (defun make-circle ()
      (list  (make-point) 1 :black))

    (defun center (circle)
      (car circle))

    (defun radius (circle)
      (cadr circle))

    (defun set-radius (circle new-rad)
      (if (> 0 new-rad)
          (format t "Polomer ma byt kladne cislo, zadali ste : ~s" new-rad)
        (setf (cadr circle) new-rad))
      circle)

    (defun movec (circle dx dy)
      (move (center circle) dx dy)
     circle)

    ;
    ; POLYGON
    ;

    (defun make-polygon ()
      (list nil :black))

    (defun items (shape)
     (car shape))

    (defun set-items (shape val)
      (setf (car shape) val)
      shape)

    (defun movep (polygon dx dy)
      (mapcar (lambda (b) (move b dx dy))  (items polygon))
      polygon)

    ;
    ; PICTURE
    ;

    (defun make-picture ()
      (list nil :black))

    ;(defun movepi (picture dx dy)) 

    ; items, set-items used for polygon and picture

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

Грубый эскиз, формы тегов:

(defun p (x y) (list x y))
(defun make-shape (type points colour data)
  (list* type points colour data))
(defmacro defshape (name args &key verify-points verify-args)
  "define the function (make-NAME points ARGS...)
to make a shape of type :NAME. Optionally 
evaluate the form VERIFY-ARGS with the
lambda-list ARGS bound and call the
function VERIFY-POINTS with the points of 
the shape, ignoring its result."
  (let ((type (intern name (symbol-package :key)))
        (fun (intern (concatenate 'String "MAKE-" name) (symbol-package name)))
        (all (gensym "ARGS"))
        (colour (gensym "COLOUR"))
        (points (gensym "POINTS")))
    `(defun ,fun (,points ,colour &rest ,all)
       (destructuring-bind ,args ,all
         ,verify-args
         ,(if verify-points `(funcall ,verify-points ,points))
         (make-shape ,type ,points ,colour ,all))))

(defun singlep (list) (and list (null (cdr list))))
(defshape point () :verify-points #'singlep
(defshape circle (radius) :verify-args (assert (realp radius) radius)
          :verify-points #'singlep)
(defshape polygon ())

Вы можете использовать это:

CL-USER> (make-circle (list (p 0 0)) :black 2)
(:CIRCLE ((0 0)) :BLACK)
CL-USER> (make-point (list (p 1 2)) :blue)
(:POINT ((1 2)) :BLUE)
CL-USER> (make-polygon (list (p 0 0) (p 0 1) (p 1 0)) :red)
(:POLYGON ((0 0) (0 1) (1 0)) :RED)

И вы можете написать несколько функций:

(defun map-points (function shape)
  (destructuring-bind (type points colour &rest data)
        shape
    (make-shape type (mapcar function points) colour data)))

И применитьони:

CL-USER> (map-points (lambda (p) (list (1+ (first p)) (second p))) '(:POLYGON ((0 0) (0 1) (1 0)) :RED))
(:POLYGON ((1 0) (1 1) (2 0)) :RED)

И решите вашу проблему:

(defun move (dx dy shape)
  (map-points (lambda (p) (destructuring-bind (x y) p (list (+ x dx) (+ y dy)))) shape))

Еще одна вещь, которую вы могли бы хотеть, это большой случай, основанный на типе (то есть CAR) формы, вашейдиспетчеризация, основанная на сопоставлении типа с чем-либо в хеш-таблице или добавлении чего-либо в его список символов.

0 голосов
/ 17 октября 2018

Ваши объекты - это просто списки, вам будет сложно различать разные виды фигур.Вы можете добавить ключевое слово, тип тега перед вашими списками (например, :point, :circle и т. Д.), Чтобы лучше распределять ваши операции перемещения в соответствии с этим тегом, но тогда это будет заново изобретать колесо, или объекты.

Простые функции и списки

одна функция, которая может перемещать все фигуры

Вы можете сделать это при условии, что вы можете отправить на фактический типобъект, с которым вы работаете.move должен знать, какая форма перемещается.Измените ваши структуры данных, если вы можете добавить тип объекта в качестве CAR для ваших списков, и использовать CASE для отправки и перемещения каждого объекта по мере необходимости.

или несколько функций с одинаковымиname.

Это невозможно, по крайней мере, в одном пакете.

CLOS

(defpackage :pic (:use :cl))
(in-package :pic)

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

(defclass has-color ()
  ((color :initarg :color :accessor color)))

Если вы не знакомы с CLOS (Common Lisp Object System), вышеописанное определяет класс с именем has-color, без суперкласса и одного слота, color.Аксессор называет и общие функции чтения и записи, так что вы можете сделать (color object), чтобы получить объект, и (setf (color object) color), чтобы установить цвет объекта на цвет.:initarg используется для определения ключевого аргумента, который должен использоваться в make-instance.

Здесь ниже мы определяем point, который имеет цвет и дополнительные x и yкоординаты.

(defclass point (has-color)
  ((x :initarg :x :accessor x)
   (y :initarg :y :accessor y)))

То же самое для круга:

(defclass circle (has-color)
  ((center :initarg :center :accessor center)
   (radius :initarg :radius :accessor radius)))

и многоугольника:

(defclass polygon (has-color)
  ((points :initarg :points :accessor points)))

Наконец, изображение представляет собой последовательность форм:

(defclass picture ()
  ((shapes :initarg :shapes :accessor shapes)))

Вы можете сделать круг следующим образом:

(make-instance 'circle
               :center (make-instance 'point :x 10 :y 30)
               :color :black))

Вы также можете определить более короткие функции конструктора, если хотите.

Теперь вы можете использовать универсальныйфункция move ваших объектов.Сначала вы определяете его с помощью DEFGENERIC, который объявляет сигнатуру обобщенной функции, а также дополнительные параметры.

(defgeneric move (object dx dy)
  (:documentation "Move OBJECT by DX and DY"))

Теперь вы можете добавить методов к этой универсальной функции, и ваша универсальная функция будет отправлять их на основе одного или нескольких специализаторов и / или классификаторов.

Например, вы перемещаете точку следующим образом:

(defmethod move ((point point) dx dy)
  (incf (x point) dx)
  (incf (y point) dy))

Вы можете видеть, что мы специализируемся move на основе класса первого параметра, здесь названного point.Метод применяется, когда значение, связанное с point, относится к классу point.Вызов INCF неявно вызывает (setf x) и (setf y), определенные выше.

Перемещение круга означает перемещение его центра:

(defmethod move ((circle circle) dx dy)
  (move (center circle) dx dy))

Вы можете специализировать универсальную функцию на любомкласс, например стандартный SEQUENCE класс.Перемещает все объекты в последовательности с одинаковыми смещениями:

(defmethod move ((sequence sequence) dx dy)
  (map () (lambda (object) (move object dx dy)) sequence))

Это полезно для полигонов:

(defmethod move ((polygon polygon) dx dy)
  (move (points polygon) dx dy))

А также для изображений:

(defmethod move ((picture picture) dx dy)
  (move (shapes picture) dx dy))

Неизменная версия

Вы также можете сделать move для создания новых экземпляров, но для этого нужно как-то сделать копии существующих объектов.Простой подход состоит в том, чтобы иметь универсальную функцию, которая заполняет целевой экземпляр экземпляром источника:

(defgeneric fill-copy (source target)
  (:method-combination progn))

Комбинация методов здесь означает, что все методы, которые удовлетворяют fill-copy, выполняются, а не только самые конкретныеодин.progn предполагает, что все методы выполняются в блоке progn, один за другим.С помощью приведенного выше определения мы можем определить простую универсальную функцию copy-object:

(defgeneric copy-object (source)
  (:method (source)
    (let ((copy (allocate-instance (class-of source))))
      (fill-copy source copy)
      copy)))

. Выше определено универсальная функция с именем copy-object, а также метод по умолчанию для объекта типа T (любойобъект).ALLOCATE-INSTANCE создает экземпляр, но не инициализирует его.Метод использует FILL-COPY для копирования значений слотов.

Например, вы можете определить, как копировать слот color любого объекта, имеющего цвет:

(defmethod fill-copy progn ((source has-color) (target has-color))
  (setf (color target) (color source)))

Обратите внимание, что у вас есть многократная диспетчеризация здесь: и исходный, и целевой объекты должны быть класса has-color для вызова метода.Комбинация методов progn позволяет распределить работу fill-copy между различными, не связанными друг с другом методами:

(defmethod fill-copy progn ((source point) (target point))
  (setf (x target) (x source))
  (setf (y target) (y source)))

Если вы дадите балл fill-copy, можно применить два метода, основанные наиерархия классов point: определенная для has-color и специализированная для класса point (для обоих аргументов).Комбинация методов progn обеспечивает выполнение обоих.

Поскольку некоторые слоты могут быть не связаны, возможно, что fill-copy завершится неудачно.Мы можем исправить это, добавив обработчик ошибок вокруг fill-copy:

(defmethod fill-copy :around (source target)
  (ignore-errors (call-next-method)))

Форма (call-next-method) вызывает другие методы (те, которые определены квалификатором progn),но мы обертываем его внутрь ignore-errors.Здесь цвет не определен, но копирование выполнено успешно:

(copy-object (make-point :x 30 :y 20))
=> #<POINT {1008480D93}>

Теперь мы можем сохранить наши существующие, мутирующие, move методы и обернуть их в :around специализированный метод, который сначала делает копию:

(defmethod move :around (object dx dy)
  ;; copy and mutate
  (let ((copy (copy-object object)))
    (prog1 copy
      (call-next-method copy dx dy))))

Чтобы увидеть, что происходит, определите метод для PRINT-OBJECT:

(defmethod print-object ((point point) stream)
  (print-unreadable-object (point stream :identity t :type t)
    (format stream "x:~a y:~a" (x point) (y point))))

И теперь перемещение точки создает новую точку:

(let ((point (make-instance 'point :x 10 :y 20)))
  (list point (move point 10 20)))

=> (#<POINT x:10 y:20 {1003F7A4F3}> #<POINT x:20 y:40 {1003F7A573}>)

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

Обратите внимание также, что вышеупомянутый подход в основном используется как способ описания различных применений CLOS, и на практике вы, вероятно, выберете тот или иной путь для перемещения точек (изменяемые или нет), или вы должны иметьразличные функции вместо одной общей (например, mut-move и move).

...