LINUX.ORG.RU

Pattern matching на чистом Си

 


0

2

Попробовал сделать алгебраический тип данных и pattern matching на чистом C без плюсов, как оказалось, это таки довольно просто: https://ideone.com/y3LQKb

Есть предложения, что можно здесь изменить/улучшить?

Можно ли совместить его с ООП? Видимо, тогда вместо enum для определения варианта надо сделать указатель на VMT?

Это нужно для собственного языка, в текущей версии компилятор переводит его в C код.

Ответ на: комментарий от peregrine

Вот щас взял и умножил на ноль глиб, гтк, ядро линукс и любую более-менее сложную программу на C.

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

Почему? Из-за синтаксиса? В моем языке будет выглядеть так:

type List[T] = case {
  Empty
  Node(value: T, next: List[T])
}

Сам pattern matching (ненужные имена можно заменять на _ ):

def List[T].sort() = case self {
  Empty -> Empty
  Node(v, n) -> n.filter(x -> x < v).sort() & v & n.filter(x -> x >= v).sort()
}

И будет разворачиваться в приведенный код на C.

Не нравятся эти if … else if…, вариантов может быть много, и хотелось бы O(1) вместо кучи проверок.

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

Я люблю простые и понятные языки - C, Oberon, Forth, Scheme с минимальным синтаксисом и основным функционалом в библиотеках. И не люблю сложные и запутанные вроде C++ или Scala, стандарты которых занимают тысячи страниц.

Но вот в том же Обероне все-таки не хватает некоторых вещей. Хотелось бы нормальных коллекций (с генериками), лямбд и пресловутого pattern maching’а (кстати, в первом Паскале зачатки алгебраического типа были - записи с вариантами), т.к. все это позволяет писать более короткий и понятный код. И немного подправить синтаксис - убрать наконец эти устаревшие ; из 70-х, сделать блоки фигурными скобками и т.д.

Эдсгер В. Дейкстра «Смиренный программист»:

Наконец, хотя этот предмет не из приятных, я должен упомянуть PL/1, язык программирования, документация которого обладает устрашающими размерами и сложностью. Использование PL/1 больше всего напоминает полет на самолете с 7000 кнопок, переключателей и рычагов в кабине. Я совершенно не представляю себе, как мы можем удерживать растущие программы в голове, когда из-за своей полнейшей вычурности язык программирования - наш основной инструмент, не так ли! - ускользает из-под контроля нашего интеллекта. И если мне понадобится описать влияние, которое PL/1 может оказывать на своих пользователей, ближайшее сравнение, которое приходит мне в голову, - это наркотик. Я помню лекцию в защиту PL/1, прочитанную на симпозиуме по языкам программирования высокого уровня человеком, который представился одним из его преданных пользователей. Но после похвал в адрес PL/1 в течение часа он умудрился попросить добавить к нему около пятидесяти новых «возможностей», не предполагая, что главный источник его проблем кроется в том, что в нем уже и так слишком уж много «возможностей». Выступающий продемонстрировал все неутешительные признаки пагубной привычки, сводящейся к тому, что он впал в состояние умственного застоя и может теперь только просить еще, еще, еще… Если FORTRAN называют детским расстройством, то PL/1, с его тенденциями роста подобно опасной опухоли, может оказаться смертельной болезнью.

Все это относится и к C++.

Den_Zurin
() автор топика

Больше ЯП, на рынке ЯП острый дефицит.

По теме - не надо аллоцировать каждую ноду, нужно держать и ресайзить один мембуф. Смотреть в сторону stb. Остальное плюс-минус неважно.

Единственно полезный сухой остаток от ООП - интерфейсы и абстрактные классы. Не надо реализовывать всю ту чушь, которую уже стопицот раз реализовали и собирать по новой все те грабли, которые уже собрали.

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

Далее следуют рассуждения о буханке и троллейбусе.

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

Node(v, n) -> n.filter(x -> x < v).sort() & v & n.filter(x -> x >= v).sort()

Я так понимаю, что filter возращает новый массив, а оператор & позволяет их склеивать. Интересно, сколько аллокаций и копирований ты делаешь на сортировку массива из 100к элементов. Интересно, как твой язык будет выглядеть, если сделать квиксорт нормально?

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

Все это относится и к C++.

Это совсем не относится к С++. У него большой стандарт из-за особенностей подхода к его описанию, плюс вся стандартная библиотека — часть стандарта. Core язык достаточно простой.

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

убрать наконец эти устаревшие

Учитывая возраст BCPL про «устаревшие» что бы то ни было я бы сильно не распространялся.

И да, Begin/End дисциплинируют, если уж на то пошло. Просто физически сложно писать лапшу, алголообразные языки поощряют выделение больших блоков кода в отдельные процедуры. В отличие от BCPL-ого семейства, которое наоборот, поощряет лапшекод.

А для конченных лентяев Питон придумали.

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

Begin/End дисциплинируют, если уж на то пошло.

Единственное, что дают Begin/End - -20% к читабельности, -40% к читабельности по диагонали. Даже подсветка не спасает.

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

Слово против слова. Кто-то и книги по диагонали читает, потом удивляясь: почему Муму написал Чехов, а памятник поставили Пушкину. А ещё бывает, что юношеские травмы от дельфячьево формоклепания сохраняются на всю жизнь и порождают ненависть к паскальному синтаксису.

Если код на алголе (паскале, обероне, модуле) сложно читать из-за begin-end, значит их там перебор.

Листаю исходники FPC - в среднем BEGIN/END в начале конце функции/процедуры, ну ещё сверху максимум два. Исходники оберона - та же ситуация.

Открываю вот исходник на яве - по пять уровней в порядке вещей.

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

А что поделать, если это костыли из легаси, которые изначально надо было не на си писать, но крестов тогда нормальных не было или вообще не было?

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

Я люблю простые и понятные языки - C, Oberon, Forth, Scheme с минимальным синтаксисом и основным функционалом в библиотеках.

А я люблю те языки, где себе в ногу тяжело стрелять. В Си это так же легко делать, как и в крестах

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

Почти наверняка сложно читать begin/end, когда код написан в манере «упихай максимум функционала в одну функцию». Ну и зачем так писать на процедурном языке, который вдобавок такому стилю нарочно препятствует.

anonymous
()

Есть предложения, что можно здесь изменить/улучшить?

Выбросить и использовать что-нибудь годное, которое для C уже есть. Ваше pattern matching не простое, а слишком простое. Узнайте, что такое PCRE и оцените простоту его реализации (которой нет).

Можно ли совместить его с ООП?

См. выше.

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

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

Iron_Bug ★★★★★
()

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

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

C++ - отвратительный язык, но ничего лучше не завезли для той ниши, где он рулит и педалит

Уже завозят тот же Rust. Хотя до этого еще был D который как раз и задумывался как лучший C++, и на мой взгляд он действительно лучше, а взлететь он не может из-за кучи уже написанного на крестах кода, который в D волшебным образом не превращается, и из-за большого числа людей, которые C++ знают и могут дорабатывать имеющуюся кодовую базу на крестах.

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

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

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

почему Муму написал Чехов, а памятник поставили Пушкину

«Муму» написал Тургенев Иван Сергеевич.

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

Уже завозят тот же Rust.

Это скорее продвинутая замена С. Попытки сделать на Rust, например, какой-нибудь GUI-тулкит выглядят просто ужасно.

Хотя до этого еще был D

С практически обязательным GC, что отпугивает многих потенциальных пользователей. А еще они долго раскачивались и упустили тут момент, когда С++ еще не развивался так активно как сейчас.

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

Ленивых вычислений пока нет, в будущем планируются.

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

Учитывая возраст BCPL про «устаревшие» что бы то ни было я бы сильно не распространялся.

Если вопрос begin/end vs. {} дискуссионный, то ; в конце оператора не несет абсолютно никакой смысловой нагрузки и ставится просто потому «так принято» (см. клетка с обезьянами, обливаемыми душем). Причем у одних операторов (return, присваивание) почему-то принято, а вот у других (for, while) - нет.

И да, Begin/End дисциплинируют, если уж на то пошло.

Сам Вирт в Обероне отказался от begin для кодовых блоков. Но в итоге вышел тот же Python с лишним end в конце (т.е. как в Ruby). Фигурные скобки четко выделяются на фоне остального кода и позволяют моментально определять начало и конец блока.

Просто физически сложно писать лапшу

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

А для конченных лентяев Питон придумали.

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

Den_Zurin
() автор топика
Ответ на: комментарий от fornlr

В процедурных языках не принято перегружать операции. Многие и в ООП считают это лишним усложнением, но, ИМХО, все же для всяких матриц и комплексных чисел удобнее писать +, -, * вместо вызовов методов.

Можно было бы, конечно, сделать string встроенным типом и задать для него + и операции сравнения, но как его реализовывать? Здесь опять возникает холивар в виде null-terminated string vs. длина в первом байте, поэтому в низкоуровневый Си это добавлять не стали. В более высокоуровневом C++ уже есть встроенный класс std:string с операциями сравнения.

Я у себя решил сделать 2 режима - safe и unsafe, в первом будут все плюшки (ООП, ФП, exception’ы, управление памятью через ARC, массивы с контролем выхода за границы), а второй будет подобно Си низкоуровневым и с указателями. В начале каждого модуля режим указывается через ключевое слово unsafe.

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

то ; в конце оператора не несет абсолютно никакой смысловой нагрузки и ставится просто потому «так принято» (см. клетка с обезьянами, обливаемыми душем). Причем у одних операторов (return, присваивание) почему-то принято, а вот у других (for, while) - нет.

Смешно, да.

while ( e 
!= false )
  return 1
+foo();
Зачем тут ; у while и как найти конец return без ';'?

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

нужно держать и ресайзить один мембуф

Это пока прототип, в окончательных версиях куча будет выделяться 1 раз при запуске (и ее размер будет задаваться, как в Java). Относительно самих алгебраических типов есть какие-нибудь предложения?

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

Сам Вирт в Обероне отказался от begin для кодовых блоков

Посмотрел EBNF - да, точно. Значит я его с модулой перепутал. Это значит END должен присутствовать в обязательном порядке, даже если требуется выполнить один оператор. Ну, кому как.

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

Любовь к ; не более чем синдром утенка

Нет, это указание на то, что текущее предписание закончено и не имеет отношение к последующим.

if a=1 then writeln('один'); // оператор закончен
if a=1 then writeln('один') // будет продолжение
else
if a=2 then writeln('два')  // оператор все еще не закончен
else writeln('не один');  // оператор завершен

Что тут не ясно, какой ещё синдром утенка?

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

Вот щас взял и умножил на ноль глиб, гтк, ядро линукс и любую более-менее сложную программу на C.

и правильно сделал!

слава БГ, что на ASM, таких вот «более-менее сложных программ» нет

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

while e != false {

Во-первых, вас просили тут указать, зачем тут может понадобиться ';', то есть один знак, а не два {}. перебор скобочек (+){+}then+end — чистая вкусовщина, но скобочки возле условия цикла позволяют заюзать булевое выражение какой угодно сложности, скажем результат присваивания. Во-вторых, вы так и не показали как одновременно не делать спец символом \n и не юзать ';' для return.

Любовь к ; не более чем синдром утенка.

Уже смешно в квадрате.

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

Вообще, обычно filter пишут в двух вариантах. Ломающем и неломающем. Или заставляют ломать filter компилятор.

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

Считаешь себя умнее, чем Одерски?

Докажи ненужность трейтов, пожалуйста, я внимательно почитаю.

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

Не думала что ты такая токсичная и упертая. Я перед началом изучения с++ прочитала историю его созлания и развития. Страструп очень четко объяснил почему программисты сишечки из AT&T так обосрались с крупным проектом, что ему пришлось изобретать новый язык.

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

Вообще, обычно filter пишут в двух вариантах. Ломающем и неломающем. Или заставляют ломать filter компилятор.

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

Мой поинт был в том, вся эта олимпиада по короткой записи базовых алгоритмов ни к чему толковому не приводит, потому что под капотом генерирует очень много лишнего, плюс: зачем фильтровать два раза, когда достаточно перераспределить in-place? Я хоть и могу представить себе правила компилятора, которые относительно соптимизируют эту кашу, но тогда семантика изменений/преобразований данных станет совсем неочевидна для программиста и скорее всего будет заточена вот под этот отдельно взятый алгоритм.

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

In 1979, Bjarne Stroustrup, a Danish computer scientist, began work on «C with Classes», the predecessor to C++. The motivation for creating a new language originated from Stroustrup’s experience in programming for his PhD thesis. Stroustrup found that Simula had features that were very helpful for large software development, but the language was too slow for practical use, while BCPL was fast but too low-level to be suitable for large software development. When Stroustrup started working in AT&T Bell Labs, he had the problem of analyzing the UNIX kernel with respect to distributed computing. Remembering his Ph.D. experience, Stroustrup set out to enhance the C language with Simula-like features.

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