LINUX.ORG.RU

Моя первая программа на Lisp

 ,


2

5

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

http://pastebin.com/CZ6MTw1S

Это преобразователь из инфиксной в префиксную форму. С учётом скобок и приоритетов операций. А ещё с возможностью вызывать функции помимо выполнения математических операций.

То есть пишем что-то вроде (c-expr ( 2 + 2 * 2 + sin ( 1 ) ) / 2 ), а оно преобразует это в нормальную лисповую форму записи - (/ (+ (+ 2 (* 2 2)) (sin 1)) 2), а такое уже легко вычисляется средствами самого Lisp.

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

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

★★★★★

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

У него лимит тредов пересчитался после увеличения лимита процессов что далеко не тоже самое что поднятие лимита тредов. С похоронами Linux Threads жесткая подвязка пидов к трэдам больше не существует. Сейчас никто не мешает поставить лимит пидов в ~100 а тредов в ~100000 и все будет пучком. Спасибо NTPL.

А вообще в отношении тредов я разделяю мнение Алана Кокса и К. Позволь мне его не цитировать.

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

There is a thread limit for linux and it can be modified runtime by writing desired limit to /proc/sys/kernel/threads-max. The default value is computed from the available system memory.

In addition to that limit, there's also another limit: /proc/sys/vm/max_map_count which limits the maximum mmapped segments and at least recent kernels will mmap memory per thread. It should be safe to increase that limit a lot if you hit it.

The limit you're hitting is lack of virtual memory in 32bit operating system. Install a 64 bit linux if your hardware supports it and you'll be fine. I can easily start 30000 threads with a stack size of 8MB. The system has a single Core 2 Duo + 8 GB of system memory (I'm using 5 GB for other stuff in the same time) and it's running 64 bit Ubuntu with kernel 2.6.32. Note that memory overcommit (/proc/sys/vm/overcommit_memory) must be allowed because otherwise system would need at least 240 GB of committable memory (sum of real memory and swap space).

If you need lots of threads and cannot use 64 bit system your only choice is to minimize the memory usage per thread to conserve virtual memory. Start with requesting as little stack as you can live with.

http://stackoverflow.com/questions/5635362/max-thread-per-process-in-linux

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

Новый вопрос: как в Emacs организовать работу над проектом? То есть если приложение состоит из нескольких файлов и среди них можно явно выделить главный, который делает load всех остальных. Нужно:

1) Быстрое переключение между открытыми файлами.

2) Команда запуска, которая будет запускать главный файл вне зависимости от того какой файл текущий.

Какие плагины для этого стоит использовать?

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

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

Советую использовать quicklisp.

При старте работы будешь делать только (ql:quickload «youproject»), после чего можно сделать in-package в свой пакет.

Сам quicklisp: https://www.quicklisp.org/beta/

Сгенерить скелет проекта: http://www.xach.com/lisp/quickproject/

1) Быстрое переключение между открытыми файлами.

C-x b

2) Команда запуска, которая будет запускать главный файл вне зависимости от того какой файл текущий.

Не совсем понял зачем.

Исправил функцию -> скомпилировал ее -> перешел в буфер slime repl и дернул главную функцию.

Если хочется одним действием, то добро пожаловать в мир elisp — функция получится в пределах пары строчек. Повесишь на любой хоткей, который нравится.

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

Проекты бывают не только на Lisp, да и в данном вопросе я не только про запуск говорю, но и про редактирование исходников. Я бы хотел что-то типа команды «Открыть проект», которая бы требовала выбрать каталог (стандартным диалогом выбора каталога). После этого должна быть возможность быстро открыть любой файл из этого каталога или любого подкаталога. В классических IDE это реализуется путём наличия боковой панельки с деревом файлов, где корень - каталог с проектом. Как это должно быть реализовано Emacs-way, я не знаю. Также было бы неплохо сохранить корневой каталог проекта в какую-нибудь переменную, чтобы можно было дёргать из скриптов для хоткеев. Чтобы работать сразу с несколькими проектами, можно использовать правило 1 окно WM (в терминах Emacs это Frame, насколько я понимаю) = 1 проект (то есть у каждого Frame своё значение переменной проекта).

Есть ли какой-нибудь готовый модуль к Emacs, реализующий подобный функционал? Мне не нужно супер-пупер наворотов в виде ручного задания списка файлов, относящихся к проекту, простое правило проект = произвольный каталог ФС, включая все его подкаталоги. Такой подход позволит работать практически с любым форматом проектов (отличаться будет только способ сборки и запуска).

Ибо продираться через всё дерево ФС, когда хочешь открыть второй файл проекта - не круто.

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

О, отличная вещь в сочетании с neotree. Спасибо.

А есть ещё что-нибудь, что можно повесить на сочетание клавиш и умеет так:

Если в корне проекта лежит Makefile, то выполнить make (нужно для C/C++ проектов), если в корне проекта лежит main.lisp, то запустить его и т. д.

?

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

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

В стандартном емаксе жамкаешь C-x C-f, в минибуфере (окошко в 1 строчку внизу) набираешь путь к нужному каталогу и Ентер. Работает дополнение по Tab. Открывается новый буфер с выбранным каталогом. В этом буфере гоняешь курсор стрелками «вверх» и «вниз». Нажатие Ентер на выбранном файле открывает его для редактирования в новом буфере, на каталоге - новый буфер с каталогом. Закрыть буфер C-x k. Переключаться между буферами либо хоткеями, либо через меню по F10.

Чё ещё надо?

Ах да! Проверил под виндой - работает: GNU Emacs 24.3.1 (i386-mingw-nt6.1.7601) of 2013-03-17 on MARVIN. Под линухом - естественно.

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

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

Ибо продираться через всё дерево ФС, когда хочешь открыть второй файл проекта - не круто.

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

(require 'neotree) // Только neotree сперва надобно установить.

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

А что в Linux (или Windows) можно сделать тысячу потоков у приложения и ядро не загнётся?

В одном недавно выпущенном сторидже известной фирмы у «главного» процесса больше 10 тысяч тредов =)

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

Если в корне проекта лежит Makefile

Alt+x compile или alt+x recompile

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

неперевелись еще идиоты? Или у них там тонкая игра?

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

То, что «можно было быстро спавнить/уничтожать и держать сотнями/тысячами» называется thread. Если по place на ядро сделал, то дальше кооперативная многозадачность thread лучше работает, чем вытесняющая от ОС (почти все операции с данными бесплатно можно считать атомарными).

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

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

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

Судя по тому, что я вижу в окрестности https://github.com/racket/racket/blob/62f5b2c4e4cdefa18fa36275074ff9fe376ddaf... , это всё есть. На компилированный код просто ссылка в новый поток передаётся.

И судя по https://github.com/racket/racket/blob/62f5b2c4e4cdefa18fa36275074ff9fe376ddaf... сериализации объектов нет. Просто передаётся ссылка на значение в поток. Если бы была сериализация, то как бы передавались shared-bytes и прочие shared-...?

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

Перл и цпп в этом плане его заруливают в путь.

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

И судя по > https://github.com/racket/racket/blob/62f5b2c4e4cdefa18fa36275074f> f9fe376ddaf... сериализации объектов нет.

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

Если бы была сериализация, то как бы передавались shared-bytes и прочие shared-...?

А для них место в памяти выделяется в специальной области. И они передаются по ссылке. Но только они.

это всё есть. На компилированный код просто ссылка в новый поток передаётся.

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

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

Это, очевидно, не так

Посмотри исходники.

в этом случае отдельные инстансы гц не могли бы работать независимо.

Почему? Если объект ушёл к child place, то он просто помечается там как используемый. Если ни один place не использует объект, он окончательно удаляется.

И они передаются по ссылке. Но только они.

Константные объекты тоже по ссылке.

Тогда я не знаю, что там выжирает память.

Изменяемые данные. Также как и в любых многопоточных системах.

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

Почему? Если объект ушёл к child place, то он просто помечается там как используемый. Если ни один place не использует объект, он окончательно удаляется.

Компактифицирующий сборщик же не удаляет объекты. Он перемещает те, что достижимы. А те, что недостижимы, просто не трогает.

Изменяемые данные.

Речь о пустых плейсах. Какие там данные?

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

Компактифицирующий сборщик же не удаляет объекты.

Там разные объекты. Подробно вот: http://docs.racket-lang.org/inside/im_memoryalloc.html

Речь о пустых плейсах. Какие там данные?

Всё, что в модулях не код, то данные. Точнее, всё, что не константно. Вот есть у тебя модуль с (define a (make-hash)) и этот хэш в каждом place станет свой.

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

Всё, что в модулях не код, то данные. Точнее, всё, что не константно. Вот есть у тебя модуль с (define a (make-hash)) и этот хэш в каждом place станет свой.

Ну я хз какие там данные и почему их так много.

Там разные объекты.

Понятно, что разные. Но если я в плейс прокинул список - то это как раз самый что ни на есть обычный объект, который будет копироваться (deep copy) при передаче в плейс. И будет две копии этого списка - одна в одном плейсе, и другая в другом. Шарятся между плейсами только упомянутые тобой ранее shared векторы, которые находятся в специальной области памяти, которая менеджится специальным, независимым от работы гц образом. Собственно:

To a first approximation, place channels support only immutable, transparent values as messages.

обрати внимание на «transparent». Именно за тем, чтобы можно было сериализовать объект для проброса в плейс.

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

Но если я в плейс прокинул список - то это как раз самый что ни на есть обычный объект, который будет копироваться (deep copy) при передаче в плейс.

Если это нормальный список из константных объектов, то не будет. А вот если это mlist или в списке строки, то он перед передачей создаст временный константный объект, который уже будет передаваться.

Именно за тем, чтобы можно было сериализовать объект для проброса в плейс.

Нет. Затем, что не-transparent в другой инкарнации модуля даже прочитать нельзя.

> (struct a (val))
> (define x (a 1))
> (a-val x)
1
> (struct a (val))
> (a-val x)
. . a-val: contract violation;
 given value instantiates a different structure type with the same name
  expected: a?
  given: #<a>

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

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

Будет, иначе сборщик мусора его не соберет. Мы по кругу ходим.

Нет. Затем, что не-transparent в другой инкарнации модуля даже прочитать нельзя.

Так и transparent такую же ошибку у тебюя выдаст:

> (struct a (val) #:transparent)

> (define x (a 1))
> x
(a 1)
> (a-val x)
1
> (struct a (val) #:transparent)
>  (a-val x)
a-val: contract violation;
 given value instantiates a different structure type with the same name
  expected: a?
  given: (a 1)
> 
transparent нужен именно из-за сериализации

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

Будет, иначе сборщик мусора его не соберет. Мы по кругу ходим.

Видимо да. Я свои аргументы (ссылки на исходник и документацию) привёл. «In addition, a master garbage collector instance holds values that are shared among places; different places can refer to memory that is allocated by the master garbage collector,».

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

transparent нужен именно из-за сериализации

> (struct a (val) #:transparent)
> (define x (a 1))
> (place-message-allowed? x)
#f

Разрешены только «immutable prefab structures containing message-allowed values».

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

Прощаю. Чем я могу помочь? Can i help you?

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

In addition, a master garbage collector instance holds values that are shared among places; different places can refer to memory that is allocated by the master garbage collector

Верно. А в shared places хранятся только шаред-векторы. Потому что предсказать, где надо хранить вновь созданный лсписок (в шаред плейсе или в локальной плейсе) нельзя (т.к. нельзя предсказать что он не будет засунут в плейс и тогда надо либо создавать ВСЕ объекты в шаред плейсе (и будет один гц на все плейсы со stop-the-world),

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

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

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

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

а ващет ты просто фигово смотрел. Посмотри в том же файле метод place_send (который и вызывается из place-channel-put), который вызывает place_async_send, который вызывает places_serialize. И в результате неудачи сериалайза бросается эррор о том, что объект является некорректным плейс-месседжем.

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

Кложа по удобству разработки почти на уровне с остальными диалектами, но завязка на жабу делает её малопригодной на задачах отличных от реализации клиент-серверного софта. В основном. Ещё Processing она заменяет на отлично, но процессинг (особенно js-имплементация) это неочень. А Ракет - пушка. Компактный и резвый, собирает в себе преимущества как и кложи, так и общелиспа. Лисп не один, их много. Что до Дедбифа, то чувак кроме CL ничего за лисп не принимает, как Лавсан, не приводя аргументов. Тому вообще бесполезно было доказывать, что CIDER уже почти на уровне со SLIME. Но пока что почти, рядом с CL кложа пока ещё остаётся недоразумением. Это особенно хорошо видно при возникновении ошибок, кложа валит нечитаемый километровый стектрейс (задолбало), CL человеческое описание возникшей проблемы и варианты действий. Если что-либо относительно компактное ваять, тут Ракет самый ок. И с гуйнёй там полный порядок. Сам бог велел на нём сосредоточиться.

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

А между тем никто так и не ответил на вопрос про производительность...

Троллишь.
Производительность чего? У тебя макрос c-expr. Он исполняется во время компиляции. Программа уже будет исполнять ту форму что сгенерировал макрос.
Это простой макрос, но есть программы где компиляция проходит очень долго, разворачивает тысячи макросов.

tp_for_my_bunghole
()

Насколько я рационально её написал

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

Вот вариант: http://folk.uio.no/jornv/infpre/infpre.html

http://folk.uio.no/jornv/infpre/infpre.lisp

tp_for_my_bunghole
()
Последнее исправление: tp_for_my_bunghole (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.