LINUX.ORG.RU

ооп и функциональщина кратко, внятно.

 , , ,


11

7

Дабы не слать напраслину на любителей создавать классы и объекты, пытаюсь разобраться в плюсах, которые отличаются от родителя, на первый взгляд, только названиями файлов, функций и приемами организации мышления погромиста. Так вот, эти ваши классы даже в учебнике называют почти структурами, а мизерное отличие сомнительного профита легко можно решить и в анси си(далее - ансися) при ближайшем обновлении. Ансися страдает перегрузкой названий функций для каждого из подлежащих обработке типов, отсутствием удобной иногда перегрузки функций, что, конечно минус, но не критично, ибо решаемо. Сиплюсик конечно удобен школьникам, тяжело принимающим всякие %s %d %x и так далее в качестве аргументов принтфов и сканфов, но зачем создавать для этого отдельный язык? Ведь << и >> становится лишним препятствием при освоении, если параллельно сдвиги битов читать. Итого, я вывел для себя, что в попытке облегчить участь программиста, разработчики языка усложнили его до степени родителя, не получив особенного профита. Чем же ооп так всем нравится, если оно не облегчает код?

★★★★★

Последнее исправление: cetjs2 (всего исправлений: 1)

Ответ на: комментарий от wota
#include <stdio.h>
#include <math.h>

int main(){
   int a = 1234;
   int b = -5678;
   int c = a+b;
   printf("Resut :%d %d\n", abs(c), a+b);
   return 0;
}

набросим еще немного. c++ унылое дерьмо.

anonymous
()
Ответ на: комментарий от anonymous
 int main() {
   mpz_class a, b, c;
 
   a = 12345678901234567890;
   b = "-5678956789567895678956789";
   c = a+b*2+a*a;

   cout << c << "\n";
   cout << abs(c) << "\n";
}

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

wota ★★
()
Ответ на: комментарий от geekless

Где ты здесь увидел противников форматных строк?

Да одни тут говорят о форматных строках как непрверяемых источниках багов, вторые каких-то валидаторов хотят... наверное, они всем сердцем за форматные строки, но маскируются.

tailgunner ★★★★★
()
Ответ на: комментарий от geekless

Ок, ок, не ершись. :D

если си с препроцессором позволяет написать свой «личный с++», то с++ (возможно, с грязными хаками и грязным применением перла) позволяет написать свой «личный хаскель» — такое маленькое, но полезное подмножество хаскеля, где тайпчек будет делать плюсовый компилятор

*лично для меня* именно в этом основное преимущество с++ — возможность делать такого рода расширения

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

похожие на алгебраические структуры

Ок, тогда что должно быть задающим множеством и что должно быть множеством операций? (Хотя это вопрос риторический).

Что бы ни было ни тем ни другим в любом случае утверждение «class Foo <==> все объекты класса Foo принадлежат множеству Foo» не опровергает.

И утверждение, что тип (с точки зрения мейнстрим-ООП, а не теории типов или сферического ООП-языка) можно считать множеством это не опровергает.

Даже если предположить, что класс задает семейство алгебраических структур.

Все что я хотел сказать, типовая сигнатура someOp(Foo foo) задает что операция someOp принимает только объекты класса Foo. А к описанию наследования не стал привлекать подтипы и подмножества. Т.к. сомневаюсь, что такие сложные вещи следует привлекать в таком простом случае. И просто уточнил что при наследовании класс-потомок наследует тип. Т.е. если A наследует B, то a одновременно принадлежит множествам A и B.

Macil ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

поэтому множество без указания допустимых операций и инвариантов вообще нельзя называть типом данных

Что-что? Во-первых, я не говорил о типах данных. Я говорил о типах в самой простой интерпретации из теории типов. Во-вторых, если что-то назвали типом, то понятно что есть некий набор термов из системы переписывания термов, которые работают с данным типом (ее я даже и не пытался определять, потому что так и не удосужились за столько лет).

Macil ★★★★★
()
Ответ на: комментарий от Macil

2. Тип. В данном конкретном случае то же самое что и множество.

а теперь давай взгляним на это с другой стороны — если у нас множества не пересекаются, то их можно удобно сделать на алгебраических типах данных

пусть у нас есть 2 множества:

ДомашнееЖивотное = { СОБАКА, КОШКА, УТКА, КУРИЦА };

ПоводырьДляСлепого = { СОБАКА, ЧЕЛОВЕК };

обращаю внимание, что элемент СОБАКА у них общий

и как это сделать на алгебраических типах данных?

и тут получается, что АлгТД это уже не множества

p.s. чтобы не было вопросов а-ля складываение элементов разных типов в одну коллекцию — можно считать, что оба этих множества это подмножества множества ЖивоеСущество

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от Macil

Что-что? Во-первых, я не говорил о типах данных. Я говорил о типах в самой простой интерпретации из теории типов.

тогда мне непонятно, каким боком типы из «самой простой интерпретации из теории типов» относятся к разговору «функциональщина vs. oop»

www_linux_org_ru ★★★★★
()

ооп и функциональщина кратко, внятно.

ООП — разрабатывать ПО. ФП — дрочить вприсядку на зигоморфизмы и смотреть на всех, как на говно.

anonymous
()
Ответ на: комментарий от Macil

Что бы ни было ни тем ни другим в любом случае утверждение «class Foo <==> все объекты класса Foo принадлежат множеству Foo» не опровергает.

множество «Металлы» с наличием дешевой операции трансмутация_ртути_в_золото и без оной это, похоже, несколько разные типы данных

ну и если перейти к практическому программированию, то множество, элементы которого это «последовательность, где можно перейти к n-ному элементу» это тоже, видимо, разные типы данных в случаях, когда переход делается за O(1) и за O(n)

«это тоже, видимо, разные типы данных» следует читать как «есть много практических задач, когда это нельзя считать одним и тем же типом данных», хотя само *множество последовательностей*, очевидно, одно и то же, независимо от того, разложена ли конкретная последовательнось в памяти в беспорядке (тогда O(n)) или в порядке (тогда О(1))

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 2)
Ответ на: комментарий от Macil

и еще про множества

я, конечно, только за, чтобы в сигнатуру функции можно было поставить «Животные\Млекопитающие», где \ означает разность множеств

на ооп это не сделать, на плюсах можно криво и ненадежно, но как тут поможет функциональщина?

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

Когда на Си пишут глючную и медленную реализацию половины Common Lisp-а — это еще куда ни шло. Потому что всегда есть возможность с сохранением наработанного кода перейти на менее глючную реализацию другой половины Common Lisp-а.

Конкретно в случае glib — это Vala, например.

А вот когда при помощи метапрограммирования из крестов делают очередной недохаскель, это уже печальнее.

На мой взгляд, любая сложная система архитектурно развивается либо в сторону ruby/perl/python-подобного кода (утиная типизация, диспетчерезация по имени метода, вот это всё), либо в сторону хаскель-подобного. Однако второй путь лично я считаю тупиковым.

К сожалению, идеология крестов такова, что она провоцирует снова и снова рождать велосипедные недохаскели. Перейдите уже на хаскель, закопайте стюардессу.

geekless ★★
()
Ответ на: комментарий от geekless

Когда на Си пишут глючную и медленную реализацию половины Common Lisp-а — это еще куда ни шло.

Кто пишет? Где? Можешь показать код?

Если ты о т.н. «десятом правиле Гриншпена» — то это отчаянный высер лиспера-петросяна, обугурченного невостребованностью маргинальщины. Реальных оснований этот высер не имеет.

anonymous
()
Ответ на: комментарий от anonymous

Да, это цитата «десятого правила», и одновременно отсылка к другой цитате, о том, что ruby — это лисп для бедных.

(Если я что-то цитирую, это не значит, что я с этим согласен, ога.)

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

Если же мы пишем на крестах, то в какой-то момент таки пора уже плюнуть, перестать обмазываться шаблонами и перейти на язык с более мощной и удобной системой вывода типов.

geekless ★★
()
Ответ на: комментарий от www_linux_org_ru

но как тут поможет функциональщина?

А где ты заметил функциональщину? Кроме того, не нравится определение в п.2, ну определи как считаешь нужным. Это намного конструктивнее (во всех, гы-гы-гы, смыслах).

Всего лишь сказал что класс можно рассматривать как множество своих объектов. И не пытался выйти даже на уровень отношений между объектами: тупая наивная теория множеств.

Ты же начинаешь мне доказывать что если люминь - металл, то он не принадлежит множеству металлы, т.к. не может использоваться в операции трансмутации в золото!

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

Кроме того, ты почему-то утверждаешь, что раз что-то является типом, то оно просто обязано иметь структуру. Я с этим не спорю, но в таком случае типы нужно рассматривать с позиции топологии, а не простой теории типов.

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

Macil ★★★★★
()
Последнее исправление: Macil (всего исправлений: 1)
Ответ на: комментарий от qulinxao

как раз таки нет , в отличии от «нормальных языков» для наследования в С++ тебе при компиляции потребуется сырцы родителя , что просто напросто «обнуляет» пользу наследования.

почему «обнуляет»? Тебе не нужно пересобирать реализацию, если она не изменилась. Т.е. если ты скажем пишешь какое-то приложение, которое использует класс «красно-чёрное дерево» в качестве наследника, то собирать это дерево тебе придётся лишь один, первый, раз. Затем линкер будет сам вставлять нужные адреса в VT, ему нужны будут лишь header'ы.

Может ты путаешь классы в C++ и шаблоны в C++? Вот шаблоны — да, каждый раз надо заново собирать. Но ведь никто жеж не заставляет их юзать когда нужно и когда не нужно.

emulek
()
Ответ на: комментарий от qulinxao

и ты бредиш когда перегрузку операторов смешиваеш с полиморфным(по типу обьекта) вызовом.

я ничего не смешиваю, а operator+() взял просто в качестве примера. Можешь его заменить на любой другой метод, например virtual void Matrix::set_sum(const Matrix &, const Matrix &) если тебе этот метод нравится больше. Ну и соответственно его считать неким наследником virtual void VoidType::set_sum(const VoidType &, const VoidType &) =0; я взял именно operator+ лишь потому, что всем должно быть ясно, что именно он делает.

emulek
()
Ответ на: комментарий от Macil

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

stevejobs ★★★★☆
()
Ответ на: комментарий от stevejobs

алгоритм типичной унификации точек доступа и избыточности

wota ★★
()
Ответ на: комментарий от tailgunner

Ну так и что ты хотел этим сказать? Фоматные строки как в си, во-первых, доступны в С++, а во-вторых, реализованы плохо, поэтому в С++ есть более безопасные средства(или даже давай придумаем типобезопасные расширяемые форматные строки и я покажу тебе такой printf на С++, на С ты его сделать не сможешь вообще).

anonymous
()
Ответ на: комментарий от stevejobs

Можешь накидать названия литературы именно вот по этому?

Не могу. Что-то есть у Карделли, что-то есть у Одерского, что-то есть в смаллтоковской «Blue Book», что-то есть в GoF.

Насколько мне известно, проблема достаточно широко изучалась исследователями ML. Сам не читал, но если и есть хоть какой-то систематизированный мануал, то это он. Причем, однозначно есть несколько разных точек зрения. Мне кажется, начать можно вот отсюда http://mythryl.org/my-Oop_Support.html

Плюс, были какие-то более ранние работы исследователей Eifel, и вроде бы даже в блоге Олега чего-то не так давно поминалось.

Macil ★★★★★
()
Ответ на: комментарий от Macil

какого именно из Олегов?) «World is a small town», особенно ЖЖ, но не настолько же )) В смысле, ссылку. ЖЖ компиляторщиков обычно интересны

stevejobs ★★★★☆
()
Последнее исправление: stevejobs (всего исправлений: 1)
Ответ на: комментарий от emulek

Тебе не нужно пересобирать реализацию, если она не изменилась

есть некоторая «излишнесть» когда при изменении(внутренем никак не видное из наследника) всё равно наследник должен пересобиратся с новым родителем.

т.е наследование излишне «связывающее» для программирования «в большом»

qulinxao ★★☆
()
Ответ на: комментарий от qulinxao

Это требуется только при изменении набора полей(т.к. объект иначе располагается в памяти). Решается за счет pimpl, например.

anonymous
()
Ответ на: ах патерны. от qulinxao

В С++ всегда нужно приседать, если автоматическое средство нельзя сделать «бесплатным». Косвенный доступ не бесплатен.

anonymous
()
Ответ на: комментарий от www_linux_org_ru

но «функциональщики» предпочитают забывать об этом, и разводить детский сад, потому что так удобнее защищать их говноязыки

Хаскели чтоле? В агде всё ок с типами «кольцо целых чисел» и «аддитивная группа целых чисел» :) А вот в C++ без концептов и аксиом с проверками — пока нет.

quasimoto ★★★★
()
Ответ на: комментарий от anonymous

abs

тогда <cstdlib> и

Resut :4444 -4444

#include <math.h>

Тогда fabs и

   printf("Resut :%d %d\n", fabs(c), a+b);
                  ~~        ^~~~~~~
                  %f
   printf("Resut :%f %d\n", fabs(c), a+b);
Resut :4444.000000 -4444

c++ унылое дерьмо

Твой пример либо вообще не собирается, либо не собирается с -Werror и ты ещё что-то говоришь про C++?

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Аксиомы с проверками там вроде никогда не планировались и не планируются. Они там для самодокументируемости и пр. Я ошибаюсь?

anonymous
()
Ответ на: комментарий от anonymous

До того как их вычеркнули в 2009 было так (n2914):

Whether an implementation replaces any expression according to an axiom is implementation-defined. With the exception of such substitutions, the presence of an axiom shall have no effect on the observable behavior of the program. [ Note: the intent of axioms is to provide a mechanism to express the semantics of concepts. Such semantic information can be used for optimization, software verification, software testing, and other program analyses and transformations, all of which are outside the scope of this International Standard. — end note ]

Ещё — http://www.stroustrup.com/C 11FAQ.html#axioms, хотя

Note that you cannot (in general) prove an axiom; we use axioms to state what we cannot prove

Это странно — недоказуемые, конечно, есть, но большинство, представляющее практический интерес, вполне доказуемы.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 2)
Ответ на: комментарий от www_linux_org_ru

class Foo <==> все объекты класса Foo принадлежат множеству Foo

ну и если перейти к практическому программированию, то множество, элементы которого это «последовательность, где можно перейти к n-ному элементу» это тоже, видимо, разные типы данных в случаях, когда переход делается за O(1) и за O(n)

первый тип: множество, элементы которого это «последовательность, где можно перейти к n-ному элементу»

второй тип: множество, элементы которого это «последовательность, где можно перейти к n-ному элементу за O(1)»

третий тип: множество, элементы которого это «последовательность, где можно перейти к n-ному элементу за O(n)»

второй и третий — подтипы (подмножества) первого.

monk ★★★★★
()

Сам пытался понять, зачем нужны "плюсы", так и не понял.

Anon
()
Ответ на: комментарий от www_linux_org_ru

т.е. по-твоему «кольцо целых чисел» и «аддитивная группа целых чисел» это один и тот же тип? множества ведь совпадают

«кольцо целых чисел», «аддитивная группа целых чисел» и «целые числа» — три разных типа. И эти множества никак не совпадают.

monk ★★★★★
()
Ответ на: комментарий от monk

А ничего, что все целые числа — тоже кольцо? Так что, вполне эти множества пересекаются. И даже могут совпадать.

Anon
()
Ответ на: комментарий от Anon

кольцо = множество элементов и множество функций (операций, отношений).

целые числа сами по себе функций не содержат

monk ★★★★★
()
Ответ на: комментарий от Anon

Идея, что «тип = алгебраическая структура» мне неясна вот чем:

Пусть есть тип «кольцо целых чисел». Алгебраическая структура включает в себя возможные операции (сложение, умножение). Пишу функцию (например, целочисленное деление). Это уже другой тип? Ведь количество операций изменилось...

monk ★★★★★
()
Ответ на: комментарий от monk

Целочисленное деление? Тип, естественно, другой будет. Но он совпадает с кольцом целых чисел с операциями сложения и умножения.

А вот если мы просто деление введем, придется расширять кольцо множеством рациональных чисел.

Anon
()
Ответ на: комментарий от Anon

Целочисленное деление? Тип, естественно, другой будет.

Вот здесь у меня программистская интуиция бунтует:

int f1(int x)
{
   return x + 1;
}

int f2(int x)
{
   return div(x,2) + 1;
}

Здесь у параметра x одинаковый тип или разный? В первом случае используется только операция сложения, а во втором, также и целочисленное деление.

Интуиция говорит, что тип одинаковый, а определение «Целочисленное деление? Тип, естественно, другой будет.»

monk ★★★★★
()
Ответ на: комментарий от monk

Здесь у параметра x одинаковый тип или разный?

Конечно одинаковый — int.

Пусть есть тип «кольцо целых чисел». Алгебраическая структура включает в себя возможные операции (сложение, умножение). Пишу функцию (например, целочисленное деление). Это уже другой тип? Ведь количество операций изменилось...

Был конкретный тип, две конкретных замкнутых бинарных операции над ним сбоку, добавилась ещё одна конкретная (почти) замкнутая бинарная операция сбоку — исходный тип остался тот же.

Вот если упаковать этот тип, эти сложение и умножение и доказательства их свойств в структуру, то эта структура будет _моделью_ (реализацией) для _теории_ (интерфейса) «кольцо». Но модель и теория это тоже типы (зависимые, вообще говоря). От теории кольца добавлением сигнатуры операции деления и утверждений о её свойствах наследуется теория кольца с целочисленным делением, любая модель, типа нашего конкретного типа с таким делением, получается как структура агрегирующая модель кольца, модель деления и доказательства его свойств.

А www_linux_org_ru говорил про множество которое carrier структуры.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

В программе данные с типом «кольцо целых чисел» бывают? Мне в качестве примера только интерфейсы из Racket вспоминаются.

monk ★★★★★
()
Ответ на: комментарий от monk

В программе данные с типом «кольцо целых чисел» бывают?

Ну данные типа int и unsigned int это также данные типа CarrierOf IntRing и CarrierOf UnsignedIntRing где IntRing и UnsignedIntRing это структуры «объекты» в которые упакованы тип, операции и доказательства свойств — элементы типа Ring представляющего собой «класс» теории, то есть интерфейс из типа, сигнатур и аксиом. И CarrierOf просто возвращает int или unsigned int. Либо Ring может не содержать тип в качестве поля (то что нельзя провернуть в C++, но можно в SML, Scala или Agda), а быть им параметризирован, тогда Ring<int>::Carrier и Ring<unsigned int>::Carrier, так что в классе остаются только операции и свойства.

Вот агда где это работает:

open import Data.Integer
open import Data.Integer.Properties
open import Relation.Binary.PropositionalEquality

open import Algebra
-- ^
-- record Ring c ℓ : Set (suc (c ⊔ ℓ)) where
--   ...
--   field
--     Carrier : Set c
--     _≈_     : Rel Carrier ℓ
--     _+_     : Op₂ Carrier
--     _*_     : Op₂ Carrier
--     -_      : Op₁ Carrier
--     0#      : Carrier
--     1#      : Carrier
--     isRing  : IsRing _≈_ _+_ _*_ -_ 0# 1#
--   ...

Ring-of-ℤ : Ring _ _
Ring-of-ℤ = record {
  Carrier = ℤ; _≈_ = _≡_; _+_ = _+_; _*_ = _*_; -_ = -_; 0# = + 0; 1# = + 1;
  isRing = Ring.isRing (CommutativeRing.ring commutativeRing)
  }

five : Ring.Carrier Ring-of-ℤ
five = + 5

record Ring это в первом приближении struct с указателями на функции в C, только тут ещё тип полем и зависимости как от него, так от его значений, плюс утверждения. Ring-of-ℤ это конкретная структура с конкретным типом, функциями и доказательствами — из неё можно добыть обратно тип так что 5 будет его элементом. При использовании типа ℤ Ring работает как интерфейс / класс типов, то есть инстанс / реализация Ring-of-ℤ подбирается автоматически, а её функции встраиваются.

То есть вещи вида «кольцо целых чисел» это дополнительные к основным типам вида «целое число» вещи — в простейшем случае можно себе представить таблицу времени компиляции или выполнения в которой с типами связаны структуры содержащие наборы функций для типа, общность таких структур описывается интерфейсами/классами, при использовании конкретного типа из конкретной структуры подставляются нужные реализации. То есть если есть 2 + 2, где 2 это точно int, а + это, допустим, _+_ : {T : Type} {addImpl : AddIface T} (T, T) -> T, то по типу T = int можно найти реализацию addImpl интерфейса AddIface и взять из неё конкретный +. Аналогично div(x, 2) просто будет искать по другому интерфейсу.

Пример с параметризацией:

#include <cassert>

template <
    class Semigroup,
    typename T = typename Semigroup::Carrier,
    T(&op)(T, T) = Semigroup::operation,
    typename UnsignedInteger>
T power_semigroup(T r, T a, UnsignedInteger n) {
    assert(n > 0);
    while (!(n % 2)) {
        a = op(a, a);
        n /= 2;
    }
    if (n == 1) return a;
    while (true) {
        if (n % 2) {
            r = op(r, a);
            if (n == 1) return r;
        }
        n /= 2;
        a = op(a, a);
    }
}

template <
    class Monoid,
    typename T = typename Monoid::Carrier,
    T identity = Monoid::identity(),
    typename UnsignedInteger>
T power_monoid(T a, UnsignedInteger n) {
    assert(n >= 0);
    return n == 0 ? identity : power_semigroup<Monoid>(identity, a, n);
}

template <
    class Group,
    typename T = typename Group::Carrier,
    T(&inverse)(T) = Group::inverse,
    typename Integer>
T power_group(T a, Integer n) {
    return n < 0 ? inverse(power_monoid<Group>(a, -n)) : power_monoid<Group>(a, n);
}

template <typename T>
struct integer_additive_group {
    typedef T Carrier;
    static constexpr Carrier identity() { return 0; }
    static Carrier inverse(Carrier x) { return -x; }
    static Carrier operation(Carrier x, Carrier y) { return x + y; }
};

template <typename T>
struct integer_multiplicative_monoid {
    typedef T Carrier;
    static constexpr Carrier identity() { return 1; }
    static Carrier operation(Carrier x, Carrier y) { return x * y; }
};

#include <cstdio>
#include <cstdlib>

int main(int, char **argv)
{
    for (int a = -2; a < atoi(argv[1]); ++a)
        for (int n = 0; n < atoi(argv[1]); ++n)
            printf("%d * %d = %d\n", a, n,
                       power_group<integer_additive_group<int>>(a, n));
}

тут видно только модели, описаний теорий не видно, так как концептов нет. Аксиом и доказательств тоже нет. Но обобщённый код завязан именно на теории, то есть на интерфейсы.

Мне в качестве примера только интерфейсы из Racket вспоминаются.

Само по себе это везде одинаково — начиная с Си, С++, Haskell, Scala, Agda. Только до агды теории интерфейсов остаются бедными.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

вещи вида «кольцо целых чисел» это дополнительные к основным типам вида «целое число»

Вот. Поэтому «основные типы» лучше трактовать как множества, а не как алгебраические структуры.

Как в Common Lisp. Например,

(declare (type (satisifies prime-p) prime-var)) 

описывает, что prime-var должна быть простым числом (в предположении, что prime-p — функция-предикат для простых чисел). И можно описывать как пред-, так и пост-условия, что какие-либо значения должны быть простыми числами. Можно такой тип в агде описать?

monk ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.