- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 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
-- Алгебраический тип-сумма Масть («перечисление»).
-- Значением типа Масть может быть одно из указанных справа
-- (или Пики, или Трефы, или Бубны, или Червы).
-- «Масть» здесь выступает конструктором _типа_,
-- а «Пики», «Трефы» и т.д. — конструкторами _данных_.
data Масть = Пики | Трефы | Бубны | Червы
-- необязательное автоматическое выведение экземпляров классов,
-- позволяющее преобразовывать значения в строки (функцией show из Show)
-- и обратно (функцией read из Read), а также сравнивать их между собой
-- (функциями классов Eq и Ord).
deriving (Show, Read, Eq, Ord)
-- Алгебраический тип-сумма Достоинство
data Достоинство = Семёрка | Восьмёрка | Девятка | Десятка
| Валет | Дама | Король | Туз
deriving (Show, Read, Eq, Ord)
-- Алгебраический тип-произведение Карта («тип-кортеж»).
-- Значения типа Карта — комбинации значений типов Достоинство и Масть,
-- объединённые конструктором данных К.
-- Часто имена конструктора данных и конструктора типа совпадают.
data Карта = К Достоинство Масть
deriving (Show, Read, Eq, Ord)
-- Синоним списка значений типа Карта.
type Рука = [Карта]
-- Функция, определяющая, есть ли в руке марьяж (король и дама одной масти).
естьМарьяж :: Рука -> Bool
естьМарьяж карты =
-- достаточно найти марьяж хотя бы одной масти
any (естьМарьяжМасти) [Пики, Трефы, Бубны, Червы]
where
-- проверить, есть ли и дама, и король заданной масти м в руке
естьМарьяжМасти м = (К Дама м) `elem` карты && (К Король м) `elem` карты
-- примеры раздач
рука = [ К Дама Трефы, К Семёрка Червы, К Король Трефы, К Туз Бубны ]
рука_без_марьяжа = [ К Десятка Пики, К Король Пики, К Дама Червы ]
main = do
проверить рука
проверить рука_без_марьяжа
проверить [] -- пустая раздача
where
проверить кк = putStrLn ( (show кк) ++ " -> " ++ (show (естьМарьяж кк)) )
-- Вывод:
-- [К Дама Трефы,К Семёрка Червы,К Король Трефы,К Туз Бубны] -> True
-- [К Десятка Пики,К Король Пики,К Дама Червы] -> False
-- [] -> False
Где тут мозги?
В глобальном масштабе это не принципиально, интерфейс модуля всё равно нужно объявлять в хедерах. А если функции уже объявлены, то определять их можно в любом порядке.
Кстати, крестовый компилятор уже не совсем однопроходный - объявления и определения внутри класса обрабатываются в два прохода, поэтому могут появляться в любом порядке.
Очень редко можно услышать о маленьких проектах на C/С++.
> кресты от этой хрени избавились
Только внутри класса и то не для всего. Для тайпдефов, например, это не работает.
При каждом изменении флажков компилятора, CI сервером при коммитах, иногда ночные билды под разные платформы.
Но тут всё же дело не столько в производительности компилятора на современных компьютерах (сейчас основным тормозом является убогий формат инклюдов), а нежелание изменять семантику языка из-за незначительных преимуществ.
Это тебе не пистончик какой-нибудь, в который изменения вносятся при зуде в левой пятке.
Это язык, изменения в котором принимаются комитетом из самых олдскульных зануд на свете. И меньше всего они хотят ломать уже написанный код или вводить значительные ограничения на работу компилятора.
Дорогой, несовместимые изменения в питон вносились только однажды. А это обратную совместимость даже не сломает.
Я уже понял, но сейчас 2015 год. Неужто это сегодня важно?
нет
Ты, безусловно, отлично знаешь язык, чтобы делать такие заявления.
Вообще говоря, не останется. Как минимум это повлиет на перегрузку функций Если сделать компилятор двухпроходным, это сломает обратную совместимость и существенно изменит семантику языка.
БЛЯДЬ! Что, реально это используется? В каком порядке были объявлены функции?
Порядок влияет на множество, из которого компилятор выбирает перегрузку. Причём на самом деле безопасней делать это именно так, как компилятор делает это сейчас. Чтобы код, который написал Вася в хедере, не перестал вдруг вести себя иначе, когда Петя добавит перегрузку после инклюда.
И да, подобные перегрузки используются очень часто.
Если нужно больше подробностей, читай The design and evolution of C++
1. Угадывать сигнатуру в момент вызова. Но тогда возможен промах: компилятор либо угадает сигнатуру, либо нет.
2. Откладывать задачу вызова функции в очередь, а при прочтении сигнатуры функции извлекать из очереди эти задачи. Потребуется дополнительная память на функции, которые используются до объявления.