Форум программистов, компьютерный форум, киберфорум
Lisp
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.73/11: Рейтинг темы: голосов - 11, средняя оценка - 4.73
1 / 1 / 0
Регистрация: 02.04.2022
Сообщений: 1
1

Множественное наследование

02.04.2022, 19:05. Показов 2207. Ответов 28

Author24 — интернет-сервис помощи студентам
Здравствуйте, объясните пожалуйста вкратце, что такое комбинирование методов в clos, что может стоять за понятием "обход соседей" и как работает call next method. Желательно объяснить как это работает в концепции множественного наследования и как в lisp разрешается проблема ромба
1
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
02.04.2022, 19:05
Ответы с готовыми решениями:

Теоретико-множественное объединение множеств элементов
мне нужно построить теоретико-множественное объединение множеств элементов. Я нашёл в этой теме...

Множественное число для английского слова
Всем привет, помогите пож-та, не очень понимаю язык лисп но очень нужно.. стоит интерпретатор...

Функция (f W), вычисляющая множественное число для английского слова
Задача:Множественное число большинства английских существительных получается путем добавления буквы...

Множественное число английских существительных
Всем привет, буду благодарен если кто-то поможет с решением задачи. Задача - Множественное число...

Множественное наследование
Есть два класса Human(fname, lname) и класс Employee c чисто виртуальными функц (зарплата и...

28
Эксперт функциональных языков программированияЭксперт Java
4486 / 2721 / 485
Регистрация: 28.04.2012
Сообщений: 8,590
03.04.2022, 08:16 2
Лучший ответ Сообщение было отмечено BetirovArtur как решение

Решение

BetirovArtur, рекомендую глянуть Art of Metaobject Protocol, ЕМНИП, там основы описаны.

Вот ещё полезная ссылка: http://www.lispworks.com/docum... 07_ffd.htm

Если вкратце, то комбинация методов позволяет указать способ решения проблемы ромба. Например имеем
– класс A с методом foo
– класс B с методом foo
– класс R, наследуемый от A и B, но не переопределяющий метод foo

Тогда, если в качестве комбинации указать +, то при вызове метода foo на объекте класса R, будут вызваны методы A.foo и B.foo их их результаты будут суммированы.

Кажется, в книге On Lisp был примерно такой пример:

Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(defgeneric price (obj)
  :method-combination +)
 
(defclass jacket () ())
 
(defmehod price ((obj jacket))
  2.3)
 
(declass shorts () ())
 
(defmethod price ((obj shorts))
  1.5)
 
(defclass suit (jacket shorts) ())
 
(format t "~a~%" (price (make-instance 'suit)))
;; 3.8
пример немного надуманный, т.к. по-хорошему костюм не является наследником брюк и пиджака, а состоит из них, но суть показывает.

Или, например комбинация progn — просто выполнит методы последовательно:

Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(defgeneric print-type (obj)
  :method-combination progn)
 
(defclass jacket () ())
 
(defmehod print-type ((obj jacket))
  (format t "Jacket~%"))
 
(declass shorts () ())
 
(defmethod print-type ((obj shorts))
  (format t "Shorts~%"))
 
(defclass suit (jacket shorts) ())
 
(print-type (make-instance 'suit))
;; Jacket
;; Shorts
При этом именно в таком порядке. Порядок можно изменить с помощью параметра :precedence-order в defgeneric, но там немного не так очевидно, как может показаться, ЕМНИП.
Не, попутал, этот параметр за другой порядок отвечает.

В общем, прям «вкратце» объяснить всё равно не получиться, читай документацию.
2
Нарушитель
79 / 75 / 16
Регистрация: 12.01.2022
Сообщений: 901
03.04.2022, 14:12 3
Цитата Сообщение от korvin_ Посмотреть сообщение
Тогда, если в качестве комбинации указать +, то при вызове метода foo на объекте класса R,
короче просто происходит перегрузка оператора.
в расте это гораздо удобнее реализовано, как мне кажется, и без всяких классов.

Добавлено через 17 минут
ща глянул - не, нехрна не удобнее
1
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
03.04.2022, 14:39 4
Цитата Сообщение от Гай Посмотреть сообщение
короче просто происходит перегрузка оператора.
Совсем нет, "+" в смысле комбинации методов и "+" в смысле сложения просто одинаково называются. Комбинация методов "+" - это когда для данной генеричной функции при вызове её метода складываются результаты вызовы методов, соответствующим классам предков. Не знаю, в каком ещё языке такое есть. Обычно, когда вызываешь метод, можно вызвать родительский метод, получить его значение. Но так, чтобы сами собой складывались значения всех родительских методов - я такое не знаею.

А функцию сложения перегружать нельзя, она не генеричная.

Онтопик: это очень обширные вопросы. Плюсую про документацию. По поводу ромба - например, пусть вызов (foo a). У класса аргумента может быть много предков, которые образуют ориентированный граф. Циклов в нём нет, потому что циклическое наследование запрещено. Весь этот граф линейно упорядочивается топологической сортировкой, и call-next-method выбирает методы вдоль этой линейно упорядоченной цепочки. То есть ромб "уплощается".
2
Эксперт функциональных языков программированияЭксперт Java
4486 / 2721 / 485
Регистрация: 28.04.2012
Сообщений: 8,590
03.04.2022, 14:55 5
Цитата Сообщение от Гай Посмотреть сообщение
короче просто происходит перегрузка оператора.
Какого оператора?
0
Нарушитель
79 / 75 / 16
Регистрация: 12.01.2022
Сообщений: 901
03.04.2022, 15:03 6
Цитата Сообщение от helter Посмотреть сообщение
складываются результаты вызовы методов
так что ли?

Добавлено через 37 секунд
Цитата Сообщение от korvin_ Посмотреть сообщение
Какого оператора?
в данном случае сложения
0
Эксперт функциональных языков программированияЭксперт Java
4486 / 2721 / 485
Регистрация: 28.04.2012
Сообщений: 8,590
03.04.2022, 16:25 7
Цитата Сообщение от Гай Посмотреть сообщение
в данном случае сложения
Оператор сложения (а точнее функция) тут не при чём и он никаким образом не перегружается. Он используется как комбинатор.

Добавлено через 21 минуту
Цитата Сообщение от Гай Посмотреть сообщение
так что ли?
Нет, не так. В Rust же нет множественного наследования.

https://onlinegdb.com/awmcOFMOI
Код
/******************************************************************************

                            Online Rust Compiler.
                Code, Compile, Run and Debug Rust program online.
Write your code in this editor and press "Run" button to execute it.

*******************************************************************************/

trait HasWeight {
    fn weight(&self) -> u32;
}

struct Cat {}

impl HasWeight for Cat {
    fn weight(&self) -> u32 {
        42
    }
}

struct Dog {}

impl HasWeight for Dog {
    fn weight(&self) -> u32 {
        9001
    }
}

struct CatDog {
    cat: Cat,
    dog: Dog,
}

impl HasWeight for CatDog {
    fn weight(&self) -> u32 {
        self.cat.weight() + self.dog.weight()
    }
}

fn main() {
    let dog = Dog{};
    let cat = Cat{};
    let catdog = CatDog{ cat: Cat{}, dog: Dog{} };
    
    println!("{}", cat.weight());
    println!("{}", dog.weight());
    println!("{}", catdog.weight());
}
https://rextester.com/NZHZ79384
Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(defgeneric weight (obj)
  (:method-combination +))
 
(defclass cat () ())
 
(defmethod weight + ((obj cat))
  42)
 
(defclass dog () ())
 
(defmethod weight + ((obj dog))
  9001)
 
(defclass cat-dog (cat dog) ())
 
(defun main ()
  (let ((cat     (make-instance 'cat))
        (dog     (make-instance 'dog))
        (cat-dog (make-instance 'cat-dog)))
    (format t "~a~%" (weight cat))
    (format t "~a~%" (weight dog))
    (format t "~a~%" (weight cat-dog))))
 
(main)
1
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
03.04.2022, 17:03 8
Раст не знаю.

Может, без множественного наследования будет понятней:
Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(defclass c1 ()
  ())
 
(defclass c2 (c1)
  ())
 
(defclass c3 (c2)
  ())
 
(defmethod foo + ((x c1))
  1)
 
(defmethod foo + ((x c2))
  2)
 
(defmethod foo + ((x c3))
  3)
 
* (foo (make-instance 'c3))
6 ; 3 + 2 + 1
Добавлено через 8 минут
Тут, наверно, первый вопрос, который приходит в голову - зачем???

Вот пример использования комбинации append (работает как +, но объединяет списки вместо сложения чисел) для сериализации в список:
Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(defclass c1 ()
  ((slot1 :initarg :slot1)))
 
(defclass c1a (c1)
  ((slot1a :initarg :slot1a)))
 
(defclass c2 ()
  ((slot2 :initarg :slot2)))
 
(defclass c (c1a c2)
  ())
 
(defgeneric to-list (x) (:method-combination append))
 
(defmethod to-list append ((x c1))
  `(:slot1 ,(slot-value x 'slot1)))
 
(defmethod to-list append ((x c1a))
  `(:slot1a ,(slot-value x 'slot1a)))
 
(defmethod to-list append ((x c2))
  `(:slot2 ,(slot-value x 'slot2)))
 
* (to-list (make-instance 'c :slot1 1 :slot1a '1a :slot2 2))
(:SLOT1A |1A| :SLOT1 1 :SLOT2 2)
1
Нарушитель
79 / 75 / 16
Регистрация: 12.01.2022
Сообщений: 901
03.04.2022, 21:01 9
Цитата Сообщение от korvin_ Посмотреть сообщение
В Rust же нет множественного наследования.
там его вообще нет, так как нет классов.
это почти то же самое, что и я написал, только я использовал уже готовый трейт и общую структуру не создавал.

Добавлено через 1 час 2 минуты
helter, ясно.
просто в других языках это называется перегрузкой операторов.
то же самое - объединяет вектора методом append, но при этом мы используем оператор сложения
тут
0
Эксперт функциональных языков программированияЭксперт Java
4486 / 2721 / 485
Регистрация: 28.04.2012
Сообщений: 8,590
03.04.2022, 21:21 10
Цитата Сообщение от Гай Посмотреть сообщение
это почти то же самое, что и я написал, только я использовал уже готовый трейт и общую структуру не создавал.
Это совсем не то же самое.

Цитата Сообщение от Гай Посмотреть сообщение
просто в других языках это называется перегрузкой операторов.
Нет, это не имеет никакого отношения к перегрузке операторов.

Вот тебе другой язык, C++:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
 
class Cat {
public:
    virtual int weight() {
        return 42;
    }
};
 
class Dog {
public:
    virtual int weight() {
        return 9001;
    }
};
 
class CatDog : public Cat, Dog {};
 
int square(int num) {
    CatDog catDog;
    std::cout << catDog.weight() << std::endl;
    return 0;
}
Что мы получаем? Ошибку компиляции:
Код
<source>: In function 'int square(int)':
<source>:22:25: error: request for member 'weight' is ambiguous
   22 |     std::cout << catDog.weight() << std::endl;
      |                         ^~~~~~
<source>:13:17: note: candidates are: 'virtual int Dog::weight()'
   13 |     virtual int weight() {
      |                 ^~~~~~
<source>:6:17: note:                 'virtual int Cat::weight()'
    6 |     virtual int weight() {
      |                 ^~~~~~
Compiler returned: 1
https://godbolt.org/z/TPac3ezes

При чём тут перегрузка операторов? Как ты решишь эту проблему перегрузкой операторов?

Вернёмся к моему примеру на Rust:
Код
...

fn printWeights(xs: Vec<&dyn HasWeight>) {
    for x in xs.iter() {
        println!("{}", x.weight());
    }
}

fn main() {
    let cat = Cat{};
    let dog = Dog{};
    let catdog = CatDog{ cat: Cat{}, dog: Dog{} };
    let mut pets: Vec<&HasWeight> = Vec::new();
    pets.push(&cat);
    pets.push(&dog);
    pets.push(&catdog);
    
    printWeights(pets);
}
https://onlinegdb.com/99qiO1C9J

Напиши такую же функцию printWeights со своей перегрузкой операторов.
0
Нарушитель
79 / 75 / 16
Регистрация: 12.01.2022
Сообщений: 901
03.04.2022, 21:57 11
Цитата Сообщение от korvin_ Посмотреть сообщение
Напиши такую же функцию printWeights со своей перегрузкой операторов.
тут нечего перегружать, так как операторов нет, а есть функция.
0
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
03.04.2022, 22:43 12
Цитата Сообщение от Гай Посмотреть сообщение
просто в других языках это называется перегрузкой операторов.
Нет. Какой тут оператор, по-вашему, перегружается? Вы можете написать на каком-нибудь языке пример, аналогичный моему с c1, c2, c3, чтобы (new C3()).foo() возвращало бы 6?
0
Нарушитель
79 / 75 / 16
Регистрация: 12.01.2022
Сообщений: 901
03.04.2022, 23:30 13
Цитата Сообщение от helter Посмотреть сообщение
чтобы (new C3()).foo() возвращало бы 6?

пжалста
0
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
04.04.2022, 00:53 14
Это не очень близкая аналогия.

1. Композиция, а не наследование.
2. У меня - три класса, здесь - четыре структуры.
3. Главное отличие, в котором суть комбинации методов: комбинация методов автоматически обходит родительские классы, а здесь сумма прописана руками.

Добавлено через 11 минут
Кстати, при тех же определениях (foo (make-instance 'c2)) вернёт 3 = 2 + 1.
1
Нарушитель
79 / 75 / 16
Регистрация: 12.01.2022
Сообщений: 901
04.04.2022, 12:31 15
helter, ну так вопрос какой был?
Цитата Сообщение от helter Посмотреть сообщение
ы можете написать на каком-нибудь языке пример
Цитата Сообщение от helter Посмотреть сообщение
чтобы (new C3()).foo() возвращало бы 6?
а как это будет сделано - не важно)
Цитата Сообщение от helter Посмотреть сообщение
Кстати, при тех же определениях (foo (make-instance 'c2)) вернёт 3 = 2 + 1.
так тоже можно сделать
0
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
04.04.2022, 13:05 16
Лучший ответ Сообщение было отмечено BetirovArtur как решение

Решение

Цитата Сообщение от Гай Посмотреть сообщение
а как это будет сделано - не важно)
Важно имитировать структуру. А шестёрку можно напечатать просто как (print 6). Смысл в чём: вот у меня цепочка наследования c1 <- c2 <- c3, для этих классов я определяю методы, возвращающие 1, 2, 3, и в применении к объекту класса c3 (а не класса c123!) генеричная функция возвращает 6, обойдя всех родителей класса и сложив значения методов. И она так же автоматически обойдёт всех родителей при любом дереве наследования.

Я не говорю, что это киллер-фича лиспа, и что по-другому никак нельзя. Но среди тех (немногочисленных) языков, о которых я имею представление, так не умеет делать никакой. В "обычном языке" даже не стоит вопрос, что при диспетчеризации метода можно сделать что-либо другое помимо выбора подходящего метода и применения его. То есть комбинирования методов, при котором результат по-разному зависит от всего дерева наследования, как раз и нет.
0
Нарушитель
79 / 75 / 16
Регистрация: 12.01.2022
Сообщений: 901
04.04.2022, 22:34 17
Цитата Сообщение от helter Посмотреть сообщение
А шестёрку можно напечатать просто как (print 6)
не надо передёргивать. вы задали конкретный вопрос
Цитата Сообщение от helter Посмотреть сообщение
Вы можете написать на каком-нибудь языке пример, аналогичный моему с c1, c2, c3, чтобы (new C3()).foo() возвращало бы 6?
я ответил.
Цитата Сообщение от helter Посмотреть сообщение
Я не говорю, что это киллер-фича лиспа, и что по-другому никак нельзя. Но среди тех (немногочисленных) языков, о которых я имею представление, так не умеет делать никакой.
Странный спор: у каждого языка свои методы. Другие языки умеют то, что не умеет лисп (какой из лиспов, кстати?).
Что ж теперь с того?
Просто, по-моему, ООП с его классами свое отжило. Многие современные языки от него уже отказались.
0
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
04.04.2022, 23:30 18
Цитата Сообщение от Гай Посмотреть сообщение
я ответил.
Это неаналогичный код.

Цитата Сообщение от Гай Посмотреть сообщение
Странный спор: у каждого языка свои методы. Другие языки умеют то, что не умеет лисп (какой из лиспов, кстати?).
Вы утверждаете, что комбинация методов аналогична перегрузке оператора. Я пытаюсь пояснить, что это не так. Что комбинация методов не перегрузка, и более того, в языках встречается нечасто. Вероятно, и вам она раньше не встречалась, и вы поэтому не понимаете, о чём речь. Но если при всём при том вы не желаете понять разницу, а предпочитаете продолжать думать, что комбинация методов есть перегрузка оператора - ваше право.
0
Нарушитель
79 / 75 / 16
Регистрация: 12.01.2022
Сообщений: 901
05.04.2022, 00:02 19
Цитата Сообщение от helter Посмотреть сообщение
Это неаналогичный код.
а почему он должен быть аналогичным?
Цитата Сообщение от helter Посмотреть сообщение
Вы утверждаете, что комбинация методов аналогична перегрузке оператора.
То что вы сначала описали достигается перегрузкой операторов. Я об этом и сказал.
0
Эксперт функциональных языков программированияЭксперт Java
4486 / 2721 / 485
Регистрация: 28.04.2012
Сообщений: 8,590
05.04.2022, 02:40 20
Цитата Сообщение от Гай Посмотреть сообщение
То что вы сначала описали достигается перегрузкой операторов.
Не достигается, просто ты не понял примера. Когда покажешь как сделать функцию printWeights с помощью перегрузки операторов, тогда и будешь говорить, что достигается.

Вот ещё пример:

Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
;gnu clisp  2.49.60
 
; ================================================================
 
(defgeneric properties (obj)
  (:method-combination append))
 
; ----------------------------------------------------------------
 
(defun delimited-list (xs &key (delimiter ""))
  (if (null xs)
      ""
      (let ((fmt (format nil "~~a~~{~a~~a~~}" delimiter)))
        (destructuring-bind (x &rest xs) xs
          (format nil fmt x xs)))))
 
(defgeneric json (obj))
 
(defmethod json ((obj (eql :null)))
  "null")
 
(defmethod json ((obj (eql :true)))
  "true")
 
(defmethod json ((obj (eql :false)))
  "false")
 
(defmethod json ((obj number))
  (format nil "~a" obj))
 
(defmethod json ((obj string))
  (format nil "~s" obj))
 
(defmethod json ((obj symbol))
  (format nil "~s" (string obj)))
 
(defmethod json ((obj list))
  (concatenate 'string "[" (delimited-list (mapcar #'json obj) :delimiter ",") "]"))
 
(defmethod json (obj)
  (let ((ps (loop :for (key val . rest)
                  :on (properties obj)
                  :by #'cddr
                  :collect (format nil "~s: ~a" (string-downcase (string key)) (json val)))))
    (concatenate 'string "{" (delimited-list ps :delimiter ", ") "}")))
 
 
 
; ================================================================
 
(defclass base-point ()
  ((x :initform 0 :initarg :x :reader point-x)
   (y :initform 0 :initarg :y :reader point-y)))
 
(defmethod properties append ((p base-point))
  `(:x ,(point-x p) :y ,(point-y p)))
 
; ----------------------------------------------------------------
 
(defclass colored ()
  ((color :initarg :color :reader color)))
 
(defmethod properties append ((obj colored))
  `(:color ,(color obj)))
 
; ----------------------------------------------------------------
 
(defclass labeled ()
  ((label :initarg :label :reader label)))
 
(defmethod properties append ((obj labeled))
  `(:label ,(label obj)))
 
; ----------------------------------------------------------------
 
(defclass enabler ()
  ((enabled :initform :true :initarg :enabled :reader enabledp)))
 
(defmethod properties append ((obj enabler))
  `(:enabled ,(enabledp obj)))
 
; ----------------------------------------------------------------
 
(defclass tagged ()
  ((tags :initform nil :initarg :tags :reader tags)))
 
(defmethod properties append ((obj tagged))
  `(:tags ,(tags obj)))
 
; ================================================================
 
 
 
(defclass point (colored labeled tagged base-point enabler) ())
 
 
 
(defun main ()
  (let ((a (make-instance 'point
                          :label "A"
                          :color 'red
                          :x 42
                          :y 9001
                          :tags '(multimethods combinations))))
    (format t "~a~%" (json a))))
 
(main)
=>
Код
{"color": "RED", "label": "A", "tags": ["MULTIMETHODS","COMBINATIONS"], "x": 42, "y": 9001, "enabled": true}
https://rextester.com/PDPAO88217
1
05.04.2022, 02:40
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
05.04.2022, 02:40
Помогаю со студенческими работами здесь

Множественное наследование
Создать иерархии наследования: телевизор, цифровое устройство - монитор. Создать динамический...

Множественное наследование
Если абстрактный класс A: class A { ...... public: virtual void display(); } От него...

множественное наследование
Почему не компилируется код? #include &lt;iostream&gt; #include &lt;conio.h&gt; #include &lt;string.h&gt; using...

Множественное наследование
Здравствуйте! Делаю следующее задание по подготовке к экзамену по С++. Пока только начал....

Множественное наследование
Приветствую, форумчане! Такое дело, получил задание на множественное наследование на С++, учу шарп...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru