LINUX.ORG.RU

Lisp: Где применимы cons?

 cons,


1

6

Для начала небольшой «бенчмарк», С без всех оптимизаций в 7406000 раз быстрее.

(defun main ()
  (declare (optimize (speed 3)))
  (let ((n 99999) (l '()) (sum 0))
    (loop for i from 0 to n
	  do (setq l (append l (list i))))
    (setq sum 0)
    (loop for i from 0 to n
	  do (setq sum (+ sum (nth i l))))
    (format t "~d~%" sum)))
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv)
{
	long n = 99999, *l = NULL, count = 0, sum = 0;
	for (long i = 0; i <= n; i++) {
		l = realloc(l, (count + 1) * sizeof(long)); 
		l[count++] = i;
	}
	for (long i = n; i >= 0; i--) sum += l[i];
	printf("%ld\n", sum);
}

Для чего же нужны cons? В качестве универсального строительного блока, я считаю это одна из самых худших структур. Все ее преимущества заканчиваются на быстром добавлении в начало. Добавление в конец уже нежелательно, разрез в произвольном месте тоже, так как нету даже быстрого доступа к случайному элементу. Она медленная и неудобная, можно придумать кучу более быстрых и удобных структур. Даже JS на световые годы опережает Lisp со своим JSON, и его частое использование лишь подтверждает его удобство.

Так почему же cons из языка-ассемблера IPL 1956 года считается важным? Да, это неплохая структура для AST, если ваша машина имеет 16 кб памяти, но она распространилась по языку слишком широко.

★★★★★

Последнее исправление: MOPKOBKA (всего исправлений: 3)
Ответ на: комментарий от alysnix

Я помню про 8 ядер говорили что ненужно для десктопа, как и 64 бита. Тогда наверное обидно было купить процессор с 8 ядрами, и увидеть что игры их не используют, только сейчас все по другому, в программы добавили работу в многопоточном режиме, и Cyberpunk 2077 показывает лучшие результаты с включенными высокопроизводительными E-ядрами. Многие научились в работу на малом количестве ядер, теперь нужно ждать следующей итерации, когда научаться работать с сотнями ядер.

Что есть житейские задачи? Какая нибудь запаковка распаковка архивов точно житейская задача, от многопоточности можно хорошо выиграть. Каждая вкладка в браузере потребляет несколько потоков, вкладок может быть много, живых. Локальные базы данных тоже выигрывают, простой пользователь может ей пользоваться через интерфейс поиска в Windows, который индексирует документы и прочее. Игры, про них уже написал, особенно всякие симуляции с кучей объектов выигрывают.

И есть куда двигаться, сейчас даже открывать картинки не научились нормально, можешь сам скачать и посмотреть за сколько откроется вот эта в твоем любимом просмотрщике изображений: https://upload.wikimedia.org/wikipedia/commons/f/fc/Pieter_Bruegel_the_Elder_...

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

короткий ответ: Лисп в 2025-м эталонное ненужно.

Менее короткий ответ. В тред набежало пионеров и прочих старпёров, которые способны разумно добро и вечно найти 100500 плюсов в любой наперед заданной древней какашке. Так-то единственный положительный момент в лиспе - это то, что его ЗДЕСЬ обсуждают спустя более чем полвека после его создания. И даже что-то пишут правда не кровавое энтерпрайзное технологическое (т.е. хоть какое-то полезное и приносящее денежку), и не более чем для примера, как у тебя, ТС.

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

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

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

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

допустим ядер 1000, а неспящих потоков в среднем 100, тогда такая система работает на 10 процентов от мощности.

малоядерная система, где ядер условно 10, но они быстрые и производительные, а неспящих потоков 100 - работает на 100 процентов от мощности. и обгоняет первую.

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

что толку что выгружаются. все равно это ядро никому не нужно, тредов всего 100, а ядер 1000.


в event-driven модели тред спит, ожидая своего события, потом его обрабатывает, потом опять спит.

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

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

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

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

А зачем больше одного потока вообще? В MS-DOS всего одно приложение активно, пользователь что, два приложения сразу будет использовать?

Появится 1000 ядер, появится и чем их занять. Сейчас то конечно много проблем с железом будущего, и настоящего. Это не только к ядрам относится, сколько сейчас работы идет из за повышении скорости дисков, когда уже неактуально держать всякие кеши?

Но 1000 ядер это ты слишком забежал вперед, следующий процессор Intel для домашних компьютеров будет иметь около 50 ядер, если они так и будут по 20 ядер добавлять в каждое поколение, то это еще надолго.

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

А зачем больше одного потока вообще?

Обрабатывать большие объемы данных.

Но 1000 ядер это ты слишком забежал вперед, следующий процессор Intel для домашних компьютеров будет иметь около 50 ядер, если они так и будут по 20 ядер добавлять в каждое поколение, то это еще надолго.

Я сегодня писал код на C++ под 10752 ядер и 86016 потоков (8 потоков на ядро). Конечно, это не CPU, а GPU, но суть та же, компьютер вполне домашний.

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

В MS-DOS всего одно приложение активно, пользователь что, два приложения сразу будет использовать?

Почему нет? Смотрит кино и читает ЛОР, например. Слушает музыку и пишет документы, просматривая инфу в инете. А ещё в фоне поддерживается передача данных по сети для других приложений.

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

Смотрит кино и читает ЛОР, например.

Одновременно? Кто-то так может? Чтобы одним глазом в сайт, вторым в кино и одновременно воспринимать?

Слушает музыку … передача данных

На это всегда были отдельные процессоры. А вот несколько центральных для одного пользователя достаточно недавно придумали.

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

Кто-то так может? Чтобы одним глазом в сайт, вторым в кино и одновременно воспринимать?

Я могу. Ещё могу слушать музыку, а сериал смотреть с субтитрами без звука, читая новости. А вот писать сообщения уже не получается.

На это всегда были отдельные процессоры. А вот несколько центральных для одного пользователя достаточно недавно придумали.

Не уловил смысла, «железу» как-то всё равно, кто/что ресурсы пользует.

mister_VA ★★
()

Если бы в Lisp реализовали cons-ячейки через функции, то была бы возможность создавать бесконечные списки, лениво-генерируемые списки, вставлять генераторные цепочки фильтров итд.

Пример:

(define (my-cons car cdr)
  (lambda (r)
    (cond
      [(eq? r 'car) car]
      [(eq? r 'cdr) cdr])))

(define (my-iota i [j 1])
  (my-cons
   j
   (cond
     [(< j i)
      (lambda (r)
        ((my-iota i (add1 j)) r))]
     [else null])))

(define lst (my-iota 3))
(println (lst 'car)) ;; 1
(println ((lst 'cdr) 'car)) ;; 2
(println (((lst 'cdr) 'cdr) 'car)) ;; 3
(println (((lst 'cdr) 'cdr) 'cdr)) ;; nil

(define lst2 (my-iota 99999999999))
(println (((lst2 'cdr) 'cdr) 'car)) ;; 3

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

сериал смотреть с субтитрами без звука, читая новости

Крут. Я два источника одновременно читать не могу. Только по очереди.

Не уловил смысла, «железу» как-то всё равно, кто/что ресурсы пользует.

Звук играет звуковая карта, а передаёт сетевая без нагрузки на центральный процессор.

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

Код бесконечным/генерируемым списком быть не может.

Может, почему нет? Даже на ассемблере можно написать код который генерирует идентичное себе продолжение. Вирусы как пример, но там не в одном массиве.

Ну и разве cons только для кода?

А если есть такой поток данных, то необходимое добавляется библиотекой.

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

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

Может, почему нет?

Потому что код — это текст, предоставленный человеком.

Даже на ассемблере можно написать код который генерирует идентичное себе продолжение.

Он генерировать будет при запуске. А до запуска он вполне конечный.

И делит код на две части, одна может работать только с cons-ячейками, вторая может работать обычно только с особой структурой.

И это нормально. Потому что программы которые работают с cons-ячейками могут опираться на то, что структура конечна (иногда допускается один цикл).

Нужна общая, используй алгоритмы для обобщённых последовательностей (в SBCL можно расширить класс cl:sequence своими типами).

monk ★★★★★
()

Вывод не окончательный, но пока думаю так:

  • Нормальные структуры данных реализуются поверх cons-ячеек относительно просто (*tree, skiplist), и даже находятся проекты которые так делают, но многие стараются избегать cons-ячейки в пользу структур в стиле С, или примитивов вроде массивов в стиле С
  • Медлительность по сравнению с другими структурами частично компенсируется низкоуровневыми деталями реализации: очень просто брать произвольный хвост и объединять в новый список, из за единого компактного размера удобно работать с ними в gc, или через эвристики подменять принцип хранения
  • (Дополняя предыдущий пункт) при неизменяемости открывается простор для параллельной работы без блокировок: дешево подменить car/cdr на новый список, некоторые более сложные структуры построенные поверх список наследуют эти преимущества, просто реализуется журнал, проще реализовывать транзакционную память.

    Считаю чем дальше, тем больше будут видны эти преимущества, станут популярнее файловые системы с лог-структурой, которые строятся на похожих принципах, и может тогда реализуется мечта Столлмана о версионированной ФС в GNU/OS
  • Просто реализуются на замыканиях без других структур, позволяя тут же строить из них генераторы и обычные списки
  • Существующие реализации Lisp не особо волнуют эти пункты, они склонны игнорировать cons ячейки за пределами простых списков из небольшого количества элементов
MOPKOBKA ★★★★★
() автор топика
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от monk

Потому что код — это текст, предоставленный человеком.

Макросы могут нагенерировать и нечто свое.

Он генерировать будет при запуске. А до запуска он вполне конечный.

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

Потому что программы которые работают с cons-ячейками могут опираться на то, что структура конечна (иногда допускается один цикл).

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

Но я не понимаю смысл разделения, не проще ли сделать проверку на входе? Сделать тип основанный на cons но ограниченный условиями? Имеет смысл ограничивать мощный тип, а не делать дублированные, разные системы.

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

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

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

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

Есть PicoLisp. В нём нет массивов. И нет компиляции. Поэтому cons-ячейки там всё: данные, код, СУБД, …

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

Не могут (в текущих реализациях что я видел)

cl:format опирается. И аналогичный Raсket’овский write.

Но я не понимаю смысл разделения, не проще ли сделать проверку на входе? Сделать тип основанный на cons но ограниченный условиями? Имеет смысл ограничивать мощный тип, а не делать дублированные, разные системы.

Вот в лиспе так и есть. Но cons структура с двумя полями, а не функция. В Haskell так, как ты хочешь. Там список пара и хвост может быть бесконечным.

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

Замыкания не сериализуются.

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

Есть PicoLisp. В нём нет массивов. И нет компиляции. Поэтому cons-ячейки там всё: данные, код, СУБД, …

Я уже его смотрел, да, там cons-ячейки пусть и представлены как есть, но из них строится все остальное. Хотя вот строки там представлены как байтовый массив где каждые 4 байта записаны в отдельный car и связаны по cdr.

cl:format опирается. И аналогичный Raсket’овский write.

А сколько опирается но не могут обработать некорректный вход? cl:format/write ничего не мешает переделать для обработки бесконечных списков, в других языках же справляются.

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

можно ее представить в виде списка cons-ячеек построенных на замыканиях.

Вот это не сериализуется

(define (my-iota i [j 1])
  (my-cons
   j
   (cond
     [(< j i)
      (lambda (r)
        ((my-iota i (add1 j)) r))]
     [else null])))

А если такое недопустимо, то достаточно обычной структуры с двумя полями, а не замыкания.

cl:format/write ничего не мешает переделать для обработки бесконечных списков, в других языках же справляются.

Например?

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

Вот это не сериализуется

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

cl:format/write ничего не мешает переделать для обработки бесконечных списков, в других языках же справляются.

Например?

Типом ограничить, или просто забить и выводить до переполнения счетчика.

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

Ну вообще, ты уже и скинул сериализованную версию. Но в какой нибудь JSON не поместишь, а надо?

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

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

Не поместишь результат iota если он бесконечный, но само замыкание сериализуется прекрасно, что в JSON что в строку.

Ты видимо говоришь о том, что если мы превращаем замыкание в cons, то сами cons то тоже замыкания, и вот такое зацикливание идет, я считаю что это нормально, функции которые занимаются экспортом в JSON внутрь структуры cons читать не должны, только их car, cdr.

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

Не поместишь результат iota если он бесконечный, но само замыкание сериализуется прекрасно, что в JSON что в строку.

(define a (iota 5))

Как из a получить сериализованное представление?

monk ★★★★★
()
Ответ на: комментарий от monk
(define (materialize lst)
  (cond
    [(not (procedure? lst)) lst]
    [(null? (lst 'cdr))
     (my-cons (lst 'car) null)]
    [(my-cons (materialize (lst 'car)) (materialize (lst 'cdr)))]))

(define (to-json lst)
  (cond
    [(null? lst) (printf "null")]
    [(procedure? lst)
     (printf "[")
     (to-json (lst 'car))
     (printf ", ")
     (to-json (lst 'cdr))
     (printf "]")]
    [else
     (printf "~a" lst)]))

(define lst (my-iota 3))
(define lst2 (materialize lst)) ;; сериализация в cons
(printf "materialize end~%") ;; сериализация в json
(to-json lst2)

Вывод:

iota call
iota call
iota call
iota call
iota call
iota call
materialize end
[1, [2, [3, null]]]

А бесконечные списки и на обычных делаются простым использованием my-cdr в вместо cdr.

Да и в ассемблере тоже, осталось пропатчить остальные функции.

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

В Racket есть #lang lazy

И делит код на #lang racket и #lang lazy.

Haskell

А в Haskell разве оператор data не создает структуру без участия замыканий? Или там все структуры строятся на замыканиях? Еще не знаком с этим языком.

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

Кстати, из этого интересное последствие. В лиспах почти везде используется левая свёртка, так как её можно сделать хвостовым вызовом:

(defun foldl/list (function value list)
  (declare (type (or function symbol) function)
           (type list list))
  (if list
      (foldl/list function (funcall function value (car list)) (cdr list))
      value))

В Haskell обычно используется правая свёртка, так как её можно использовать на бесконечных списках

foldr f init [] = init
foldr f init (x:xs) = f x (foldr init xs)
monk ★★★★★
()
Ответ на: комментарий от MOPKOBKA

сколько людей за прошлый век пыталось придумать нормальную однозначную математическую нотацию?

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

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

можешь картинок загуглить, или эту одну главу почитать (с картинками интереснее):

например nomath3.gif image025.png vk.com/wall-36507793_155073

12_30790_universalniy-yazik-i-teoriya-znaka.html : image021.png image022.png image023.png image024.png image025.png image026.png

блисссимволика: image032.png

ради кого? радикалы

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

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

Если бы в Lisp реализовали cons-ячейки через функции, то была бы возможность создавать бесконечные списки, лениво-генерируемые списки, вставлять генераторные цепочки фильтров итд.

см. Nepeivoda_N.N.,_Skopin_I.N._Osnovaniya_programmi(libcats.org).pdf:

стр 541

§ 9.4. РЕКУРСИВНЫЕ СТРУКТУРЫ ДАННЫХ

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

Обсуждение методологических ошибок Lisp’а проведено в следующем разделе.

Формальную осмысленность списочных структур относительно существующего аппарата распределения памяти, в отличие от рекурсивных процедур,
легко задать чисто формальными правилами, впервые точно сформулированными в Алголе 68.
1. Любой рекурсивно определяемый ссылочный тип осмыслен, поскольку он имеет универсальный элемент nil и поскольку память, отведенная
на указатель, имеет стандартный размер.
2. Рекурсивное использование типа осмысленно, если при раскрытии системы конструкторов на любом пути от типа к его рекурсивному использованию встретится осмысленный тип.
Таким образом, рекурсивные структуры существеннейшим образом задействуют аппарат указателей.

стр 544

9.4.1. Списочные структуры

Списки являются одной из важнейших ‘реальных’ математических структур, используемых в программировании. Абстрактное определение списков
элеентов произвольной природы см. в Определении A.6.1.
В современном программировании наиболее распространенной реализацией этого абстрактного определения являются списки в стиле языка LISP,
которые строятся с помощью следующей конструкции (T — имя типа элементов):
Sequence(T) = (Head : T, Tail : *Sequence(T)); (9.6)
Выбор этих двух компонент производится по их именам. Как было сказано
в предыдущем параграфе, для корректности такой конструкции требуется,
чтобы рекурсивная ссылка на список была заменена ссылкой на указатель
(ref T — Алгол 68, ^T — Pascal,*T — C++/C#)

Иначе конструкция понималась бы как требование на бесконечную память для потенциально бесконечной графовой структуры.

ну и смотри в алгольную реализацию простого интерпретатора лиспа:

тут => en.algol-68-genie.lisp-interpreter.a68 :

видишь стр 37-35 и ниже до стр 66?

это OP CONS = (VALUE v, w) VALUE: HEAP NODE := (v, w), PRIO CONS = 9; собственно они и есть.

насчет бесконечных списочных структур над cons-ячейками смотри в той же книге стр 546 про кольцевой список и вообще

§ 9.4. РЕКУРСИВНЫЕ СТРУКТУРЫ ДАННЫХ

еще там концептуально вставляют приложения, например:

§ B.8. ОСНОВНЫЕ ПОНЯТИЯ НЕФОРМАЛИЗУЕМОСТИ

и «о формализации неформализуемых понятий» Н.В. Белякина

кстати, зацени кольцевую диагарму на стр 892

§ C.15. ХИМЕРЫ И ВЫМЫСЛЫ

Рис. C.1. Сферы Белосельского-Белозерского

вот оне сама суть– диагармы метапроговы.

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

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

Если бы в Lisp реализовали cons-ячейки через функции, то была бы возможность создавать

first class objects: first class bindings, first class environments – те же словари форта, например.

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

…/history-of-computing/

History of Manpages

про troff runoff BCPL, plan9 utf-8, heirloom troff, groff реализации

не упомянуто: уникодная neatroff и awf реализация на awk от автора amazing_awk_assembler и http://doc.cat-v.org/henry_spencer/ifdef_considered_harmful и десяти заповедей для С программеров

BCPL+ASM реализация runoff где-то совсем в анналах

самые технологически продвинутые по качеству (например, алгоритм переносов и выравнивания пустых мест из tex) – это heirloom troff, neatroff, plan9 troff, groff mom набор макросов похож на markdown, например

mandoc, mdoc: на markdown похож mdoc набор макросов для troff

презентации writing-software-documentation-with-mdoc7

мастер-классы и туториалы : экзерцисы mdoc и прэсса:

bsdcan18-mandoc . (roff | pdf) , eurobsdcon2014-mandoc-slides , eurobsdcon2014-mandoc-paper . (pdf |roff |tar.gz->.roff)

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

на том же troff/heirloom troff, например

язык разметки в целом даже структурный и семантический

макросы mdoc/mom а не ms семантические над голым troff/groff – примерно так же, как latex над plain tex (кстати, в книжке непейводы и скопина в целом архитектура tex неплохо написана, и есть ссылка на «TeX:the Program and the book» <=> «Все о TeX» )

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

так что далее нужно какой-то со складками fe/fte/the/XEDIT ISPF/KEDIT брать и в складках фолдинговых расписывать

мануалы метапроговы и диаграмы со жгутиками и проводочкаме в духе «Programming the problem oriented language» POL.pdf или конечных автоматов (можно хоть руками на таблице решений, хоть на XSLT как в той книжке хоть на Libero : зацени кобольное calcpk.cob и апплетово calcexpr.htm и прочие пару десятков реализаций на настраиваемых шаблонах с одной схемы связей конечного автомата

в общем, метапроги надобно писать в манул-ориентированном грамотно-литературно программируемом стиле.

иначе ни схемки ни чертежика и непонятно как диагармы копулируют со жгутиками и проводочкаме… ;-(((

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

уникодная neatroff реализация:

github сайт https://litcave.rudi.ir/ :

>TYPESETTING

For a guide to set up Neatroff, see getting started with Neatroff. For the list of features and new requests, see Neatroff introduction.

  • Here’s the port of Plan 9 Troff (troff, tr2ps, eqn, tbl, pic, and grap) to Linux. The preprocessors and the macro packages can be used with Neatroff also.
anonymous
()
Ответ на: комментарий от anonymous

neattroff-диагармы постскриптовые метапроговые

neatroff.pdf стр. 15:

Source Code Organization

The following figure shows where Neatroff’s major layers and features are implemented in its source tree.

отрисовывается кодом на troff со стр. 543:

neatroff.ms#L543

где все эти стрелочки, коробочки – и жгутики и проводочки

осталось только диагармер организовать где копулируют в подфункции

anonymous
()