[haskel] шаблон разрабоктки

yroslavasako

Нужно сгенерировать структуры данных (экземпляры алгебраического типа которые могут быть связаны между собой. Их связи между собой напоминают граф. Если бы были деревьями - я бы просто сделал рекурсию.
В случае c++ я бы сделал несколько полей для хранения указателей на другие экзмепляры данного типы. По мере создания элементов я просто связываю их между собой. А как лучше всего это в хаскеле сделать, чтобы в конечное время уложиться?

apl13

Ня?

yroslavasako

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

apl13

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

apl13

Алсо, а чем "сложная структура связанных объектов" тебе не граф? Тем более, что Data.Graph вполне себе направленный.

yroslavasako

ну там ещё есть и Data.Map
я думал может что более клёвое есть. Какой-нибудь необычный приём.

yroslavasako

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

bleyman

Когда-то давно видел программку на хаскеле, которая реализовывала простенький ассемблер, помимо всего прочего с расстановкой конкретных оффсетов для джампов по лабелям, в том числе и вперёд, но была как-то так дико хитроумно написана, что делала это за один проход (как будто). То есть там был мап "лабели - адреса", заполнявшийся в процессе прохода, а кодогенератор естественно генерил код лениво, так что когда наконец нужна была инструкция "сделать джамп туда-то", этот адрес в мапе уже был.
К сожалению, моё гугл-фу оказалось бессильно в данном случае, так что я не могу найти то, о чём говорю =(

yroslavasako

у меня получилось что-то вроде этого

data ComplicatedEntity = ComplicatedEntity {
cID :: Int,
neighbours :: [Int],
parent :: Int,
otherRelations :: [Int],

-- ...someOtherUsefulFunctions
}

type EntityIndex = Data.Map.Map Int ComplicatedEntity
type CollectedEntity = (EntityIndex,Int)

createComplicatedEntity :: ComplicatedEntity → CollectedEntity → CollectedEntity
createComplicatedEntity x s = Data.Map.insert (cID x) x s

neighbours' :: CollectedEntity → [CollectedEntity]
neighbours' (s,i) = map (λx→ (s,x (neighbours (Data.Map.(!) s i

parent' :: CollectedEntity → CollectedEntity
parent' (s,i) = (s,parent $ Data.Map.(!) s i)

otherRelations' :: CollectedEntity → [CollectedEntity]
otherRelations' (s,i) = map (λx→ (s,x (otherRelations (Data.Map.(!) s i

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

yroslavasako

У меня идёт ломка: нужно писать на хаскеле, а хочется использовать уже известные мне парадигмы.
Объектный подход - одна их тех вещей, которой я не смог найти замену. Я тут говорю не об объектно-ориентированном подходе и его сложностях и уловках: инкапсуляции и наследовании. Я говорю о самом принципе введения в программу объектов для моделирования сложных процессов. При этом оказывается необходимо написать несколько процедур (или функций-членов которые работают с одним определённым типом (классом) объектов. Остальные объекты их не касаются, необходимые изменения состояний тех объектов записывается отдельными функциями. В случае хаскеля не получается ввести объекты, связанные по ссылке; все шаблоны проектирования, основывающиеся на типе связи "использование" оказываются недействительными. В том же pythonе, например, тоже не пишут шаблонов как в яве или плюсах, но там соответствующий шаблон проектирования можно реализовать, не используя специфический шаблон структуры данных. В хаскеле (и видимо любом чистом функциональном языке) это не возможно. Вместо работы с отдельными структурами данных и соответствующей декомпозиции (пишем всё необходимое для модификации данного класса, всё остальное можно отнести независимо к другому классу) каждый раз, желая изменить моделируемую систему, приходится обрабатывать весь мир вместо отдельного объекта.
Предыдущий пример не самый печальный: в нём был всего один тип. Более сложные системы дают более неудобоваримые примеры, содержащие несколько разных типов и несколько индексов. Допустим мне нужно смоделировать дорожное движение по городу. Я заведу объекты типа "дорога", "перекрёсток", "автомобиль"; ещё быть может введу понятие "шедулер светофоров", если они могут управляться из программы. Я инициализирую нужное количество объектов описанных выше типов и свяжу их между собой через ссылки: дорогу с перекрёстками, которые на ней находятся, дорогу с автомобилями, едущими по ней, перекрёстки с дорогами, их образующими, перекрёстки с машинами, ожидающие очереди на них, а сами автомобили свяжу либо с дорогой, по которой они едят, либо с перекрёстками, где они стоят или поворачивают. И для всего этого нужно использовать указатели, которых в хаскеле нет. Вложения, которые мне в хаскеле доступны, мне не помогут.
Какой подход мне использовать, чтобы окончательно не пасть духом? Какая замена традиционным шаблонам существует? Конечно, на динамиках и mapах я мог бы реализовать указатели, но это сильно не тру. Какие ещё варианты есть?
update: да ещё можно просто заюзать объектные базы данных, но это тоже через жопу.

apl13

У меня идёт ломка: нужно писать на хаскеле, а хочется использовать уже известные мне парадигмы.
Я сегодня обнаружил, что так долго баловался хаскелем, что теперь программу на сях (с состоянием и сайд-эффектами) пишу так, будто сперва ее сделал на хаскеле с монадами, а теперь переписываю.
Объектный подход - одна их тех вещей, которой я не смог найти замену.
Типа, классы с монадами и первоклассность функций не спасут отца русского ООП? Есть ведь еще объектный хаскель для эстетов.
Допустим мне нужно смоделировать дорожное движение по городу. Я заведу объекты типа "дорога", "перекрёсток", "автомобиль"; ещё быть может введу понятие "шедулер светофоров", если они могут управляться из программы. Я инициализирую нужное количество объектов описанных выше типов и свяжу их между собой через ссылки: дорогу с перекрёстками, которые на ней находятся, дорогу с автомобилями, едущими по ней, перекрёстки с дорогами, их образующими, перекрёстки с машинами, ожидающие очереди на них, а сами автомобили свяжу либо с дорогой, по которой они едят, либо с перекрёстками, где они стоят или поворачивают.
Во-первых, ты рассуждаешь строго. Если в итоге нужно померять интенсивность движения во вторник с трех до пяти на перекрестке улиц Ленина и Чернышевского, то естественно, что автомобиль, по утрам перевозящий молоко с молкомбината в магазин в поселке Мелиораторов, тебе не надо заводить. Ибо лень.

yroslavasako

я так понял тебе отсутствие референсов не мешает. Можешь пояснить как бы ты стал моделировать указанную ситуацию?

apl13

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

yroslavasako

Во-первых, Data.Dynamic - это страшное зло, чреватое всеми возможными ошибками типизации. Использовать их в хаскеле можно только в целях академической фалометрии. Потому что если пустить Dynamic в широкую практику, то непонятно, зачем вообще вы выбрали хаскель в качестве языка реализации.
Задачу усложню: известна форма дорог (искривлённость в разных точках, количество полос есть статистические данные о скорости прохождения различных участов дорог в зависимости от его формы, так же известна вероятность аварии в зависимости от количества транспорта и от формы участка дороги, причём с разной вероятностью происходят неприятность различной степени: от блокирования одной полосы до полной остановки дороги в обоих направлениях. Далее известны режимы работы светофоров, законы регулировки движения автомобилей по светофору (первые 2 секунды красного - это ещё жёлтый). Для самих автомобилей известна функция оптимальности маршрута: взвешенная комбинация суммарного расстояния и времени в пути. Для расчёта маршрута и оптимизации функции автомобили используют либо 1) информацию о текущей ситуации на дорогах и пробках на ней с отставанием в n минут, либо 2) усреднённую информацию за некоторый период в прошлом на тех же улицах. Сами автомобили порождаются в случайном порядке для симуляции беспорядочного трафика: город разбивается на несколько доменов, между ними на основании статистики составляется расписание движения: например, в период 8:00 - 9:00 из пункта A в пункт B необходимо запустить U (нормальное распределение) автомобилей, для этих автомобилей случайно выбирается адрес отправления из домена A и назначения из домена B и запускается автомобиль для эмуляции.
можно ещё произвести дифференциацию автотранспорта.
Задачи
1) Обкатать модель на известных данных по прошлым годам: статистическое распределение движения, зарегистрированные пробки, заданные схемы работы светофоров
2) Подкрутить коэффициенты модели с тем, чтобы она лучше соответствовала действительности.
3) Сделать краткосрочный прогноз развития пробок в заданной конфигурации города при текущих алгоритмах светофорной маршрутизации.
4) Рассчитать субоптимальные параметры управления светофорами для минимизации пробок (либо максимизации потока)
5) Предложить наиболее эффективный план развития автомагистралей города.

apl13

yroslavasako

гуголь по запросу
haskell aggregation relation
и по запросу
haskell aggregation template
ничего путного не даёт. Неужели ещё никто не написал обёртки на template haskell для этого (приведённого выше мною) бойлерплейт кода?

apl13

Гм.
Давайте фантазировать! :party2:
известна форма дорог (искривлённость в разных точках, количество полос)
data StreetAddress
roadCurvature :: StreetAddress -> Double
laneAmount :: StreetAddress -> Integer

есть статистические данные о скорости прохождения различных участов дорог в зависимости от его формы, так же известна вероятность аварии в зависимости от количества транспорта и от формы участка дороги, причём с разной вероятностью происходят неприятность различной степени: от блокирования одной полосы до полной остановки дороги в обоих направлениях
type Probability = Double
type Distr = Double -> Double

empiricalSpeedDistr :: Double -> Distr

type AccidentProbability = (Integer, Integer) -> Double -> Probability

laneLockProb :: AccidentProbability
roadBlockProb :: AccidentProbability

data Emergency = LaneLock | RoadBlock | WarWithSoviets | Gozilla | HugeAsteroidsFalling | MartiansInvading

accidentProb :: Emergency -> AccidentProbability
accidentProb LaneLock = laneLockProb
accidentProb RoadBlock = roadBlockProb
accidentProb _ _ _ = 1.0

Далее известны режимы работы светофоров, законы регулировки движения автомобилей по светофору (первые 2 секунды красного - это ещё жёлтый)
data TrafficLight = Red | Yellow | Green

trafficLight :: StreetAddress -> Time -> TrafficLight

Для самих автомобилей известна функция оптимальности маршрута: взвешенная комбинация суммарного расстояния и времени в пути
type Route = [StreetAddress]

stepArrival :: Time -> StreetAddress -> StreetAddress -> Time

phaseJump :: (Time, StreetAddress) -> StreetAddress -> (Time, StreetAddress)
phaseJump (t0, a0) a1 = (stepArrival t0 a0 a1, a1)

routeArrival :: Time -> Route -> Time
routeArrival t0 r = fst $ foldl phaseJump (t0, head r) (tail r)

Для расчёта маршрута и оптимизации функции автомобили используют либо 1) информацию о текущей ситуации на дорогах и пробках на ней с отставанием в n минут, либо 2) усреднённую информацию за некоторый период в прошлом на тех же улицах
type TrafficInfo = Emergency -> AccidentProbability Time -> StreetAddress -> StreetAddress -> Time)

exactInfo :: Time -> TrafficInfo

n :: Time

recentInfo :: Time -> TrafficInfo
recentInfo = exactInfo . (\x -> x - n)

statisticalInfo :: Time -> TrafficInfo

Сами автомобили порождаются в случайном порядке для симуляции беспорядочного трафика: город разбивается на несколько доменов, между ними на основании статистики составляется расписание движения: например, в период 8:00 - 9:00 из пункта A в пункт B необходимо запустить U (нормальное распределение) автомобилей, для этих автомобилей случайно выбирается адрес отправления из домена A и назначения из домена B и запускается автомобиль для эмуляции.
можно ещё произвести дифференциацию автотранспорта.
data VehicleType
type Ghetto = [StreetAddress]
data TrafficBeast = Vehicle VehicleType StreetAddress StreetAddress
randomVehicle :: (Time, Time) -> Ghetto -> Ghetto -> (Time, TrafficBeast)
optimalRoute, usualRoute :: (Time, TrafficBeast) -> Route

Ня? :4u:

apl13

После чего мне пришло в голову, что все это ваше ООП есть просто попытка ввести первоклассность функций, предпринятая не с того конца. Вместо того чтобы ввести в язык автоматические действия над функциями (без необходимости явного определения результата в коде мы создаем объекты, между которыми и выполняются эти автоматические действия. В журналах пятнадцать лет назад писали как? Объект есть такая сущность, которой присущи методы. А если, воспользовавшись принципом относительности, вывернуть эту картину наизнанку и посмотреть изнутри, то получится, что объект есть просто маскировка для функции, первоклассная, по нормам языка, обертка для нее. Существование в STL всяких квазифункциональных шаблонов, типа фурыча, это, на мой взгляд, подтверждает; да и сама STL есть, собственно, многоумное определение вещей, которые в хаскеле сразу же определены функционально еще в Прелюдии.

yroslavasako

не говори гоп. Всё равно не получается упростить разработку. У тебя не столько ответ на вопрос, сколько уход от него. Хотя это, конечно, не отменяет тех усилий, которые ты потратил
Лучше посмотрим на то, что ты предложил мне для изучения.
data Emergency = LaneLock | RoadBlock | WarWithSoviets | Gozilla | HugeAsteroidsFalling | MartiansInvading
лучше уж тогда
data Emergencey = LaneLock Int | RoadBlock

Поскольку всё остальное нам моделировать не нужно, а перекрываться может несколько линий движения, а не только одна
data TrafficLight = Red | Yellow | Green
абсолютно неверный подход. В пределах перекрёстка должны определяться правила, которые определяют кому куда можно ехать, а светофоры уже потом ставятся в достаточном количестве, чтобы обеспечить донесение этих правил до водителей. То есть хранить нужно лишь пары (улиц, с которых и на которые разрешён переезд).
Просмотрев решение, я так понял, что ты решил отказаться от понятия улица и перекрёсток в пользу понятия участок движения.
Но самого интересного у тебя нет - собственно моделирования.
В чём я вижу возникающие перед тобой проблемы. Для моделирования нужно определять находящиеся рядом машины. А сделать это можно только имея список этих машин рядом с собой. Для этого их нужно ассоциировать с участками дороги, на каждой из которых вводить криволинейные координаты. И если каждой машине поставить в соответствие участок дороги, то обратного сделать уже не получится: придётся либо каждый раз искать среди всех машин соседей, либо составить индекс. А составление индекса - это один кривых способов реализовать агрегацию (возможно он будет выглядеть не так страшно, если boilerplate замаскировать template haskell).
Иными словами есть порочный круг взаимосвязанных знаний: Vechile -> StreetAddress, StreetAddress -> Vechile. Такую структуру мы не можем никак инициализировать, она бесконечна. В нефункциональных языках этот круг разрывают через указатели: указатели не обязаны указывать на уже инициализированную переменную, они могут быть пусты. В разных местах через указатель появляется ссылка на один и тот же элемент. Всё это на хаскеле можно сделать через индексы (Data.Map) дял хранения данных и идентификаторы, заменяющие указатели.
Но эта конструкция слишком перегружена. Поэтому хотелось бы либо
1) Найти более органичный путь решения проблемы цикличных зависимостей
2) Найти способ заменить все шаблоны дизайна разработки, связанные с отношением агрегации.
То, что ты привёл выше, не показывает пути решения моей задачи, которое было бы проще написанного, скажем, на питоне и даже на плюсах. Потому что если обычно программы на процедурных и ООП языках содержат в себе некоторую неполную реализацию ФЯ, то в данном случае в программе на ФЯ придётся писать реализацию некоторых возможностей процедурных языков.

apl13

Ну екарный бабай.
Просмотрев решение, я так понял, что ты решил отказаться от понятия улица и перекрёсток в пользу понятия участок движения.
Ты решил отказаться от понимания того, что я не упомянул специально про функции
neighbourhood :: StreetAddress -> [StreetAddress]
isCrossing :: StreetAddress -> Bool

Что дает нам связность города.
Кроме того
type Street = [StreetAddress]

Ну и
greenLight :: Time -> StreetAddress -> StreetAddress -> StreetAddress -> Maybe Bool
greenLight t a0 a1 a2 = if (not $ isCrossing a0) || (any (not . `elem` (neighbourhood a0 [a1, a2]) then Nothing else Just (trafficSchedule t a0 a1 a2)

придётся либо каждый раз искать среди всех машин соседей
Ну да, и ч0?
data Teapot = Tp TrafficBeast StreetAddress
type Traffic = [Teapot]

passingBy :: Traffic -> StreetAddres -> [TrafficBeast]
passingBy tf a = [tb|Tp tb a <- tf]

goats :: Traffic -> Teapot -> [TrafficBeast]
goats tf (Tp _ a) = concatMap (passingBy (tf \\ tp a:(neighbourhood a)

Кому сейчас легко?
И вот еще:
trafficIteration :: Traffic -> Traffic
trafficIteration = map phaseJump

Где phaseJump уже не тот, что прежде, он переопределен с использованием goats.

yroslavasako

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

apl13

Вообще, тебе нужно либо перестать мыслить объектно, либо вернуться к плюсам. Понятно, что настоящий программист на фортране и на хаскеле будет писать программы на фортране. Но все-таки каждому языку своя парадигма; и если тебе удобнее объекты - пиши на объектном языке. Моцарта тоже можно играть на пиле, но на клавесине все-таки удобнее.
Да, мне кажется, в твоей задаче объектный подход более натянутый и искусственный, чем функциональный. Вот тоже - как в объектном стиле ты будешь искать соседей у машины? Такая связь между ними существует секунды - во всяком случае, между встречными и на однополосных дорогах; а чтобы найти, с кем машина будет по соседству в следующий момент, все равно надо весь пул шерстить.

yroslavasako

В моём подходе на каждой итерации машина будет запрашивать у дороги состояние движения вокруг неё, потом будет выдавать дороге указания как она будет себя вести (вектор скорости наверное).

Ivan8209

Я вот пытаюсь понять, не ищешь ли ты "functional record update"
или может быть ты вообще хочешь "пролог"?
---
"Утверждаю, что с научной точки зрения, главное в профессии вора,
как и в профессии святого, конечно, это вовремя скрыться."

yroslavasako

Я вот пытаюсь понять, не ищешь ли ты "functional record update"
да не, вроде не оно. Первые двадцать ссылок гугла точно не о том.
Оставить комментарий
Имя или ник:
Комментарий: