Как стать автором
Обновить

Комментарии 23

я понимаю, почему на момент написание этого моего комментария здесь не было ни одного другого.
лисп – он как коммунизм, его время ещё не пришло, но он всегда на горизонте, а мы к нему дружно идём.
Решить эту проблему можно многими способами, например, ввести уникальный маркер и вставлять новые команды после него. Маркер будет представлять собой невыполнимое условие, которое мы добавим после cond

Заведомо невыполнимую ветку для cond проще записать как

(nil 'secret-marker)

Экономится один вызов eq при вызове, да и читать проще.
Согласен. Спасибо за замечание, упустил как-то эту очевидную деталь.
Хотя нет, при вызове ничего не экономится, компилятор же эту строчку все равно выбросит как заведомо невыполнимую.

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

Возвращает-то она, по сути, не лямбда-функцию, а s-выражение, являющееся лисп-кодом. Возврат лямбда-функции — это, например

(defun return-adder (n)
  (lambda (x) (+ x n)))

(defun add3 (x)
  (funcall (return-adder 3) x))

Здесь return-adder возвращает лямбда-функцию, которая используется в функции add3.

Соответственно, вы можете start-program вполне определить через defconstant или простым setf.
Хотя нет, при вызове ничего не экономится, компилятор же эту строчку все равно выбросит как заведомо невыполнимую.

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

Возвращает-то она, по сути, не лямбда-функцию, а s-выражение, являющееся лисп-кодом.

Да. Quote там нужен, чтобы выводился именно код, а не что-то вроде #<FUNCTION (LAMBDA (X)) {100A741ECB}>.

Соответственно, вы можете start-program вполне определить через defconstant или простым setf.

Изначально подразумевались еще некоторые параметры для выбора варианта начального кода, поэтому осталось defun…
Все равно, ему как минимум придется рассмотреть многократно эту ситуацию во время выполнения программы, даже если фактически он не будет выполнять эту операцию. Ведь Лиспу совершенно неизвестно, что скормят ему в eval.

Да, получается, компилятору больше работы на понимание того, что (eq t nil) и просто nil есть одно и то же. Т.е. экономия все-таки есть. Вот ведь жаркий спор с самим собой вышел.
Quote там нужен, чтобы выводился именно код, а не что-то вроде #<FUNCTION (LAMBDA (X)) {100A741ECB}>

А до меня это дошло только после того, как запустил (start-program) в toplevel. До этого никак не мог понять, как это переменная инициируется значением, получаемым после применения функции к этой самой переменной. Картинка с Уроборосом, конечно, очень в тему тут.
(Не холиваров ради, правды для) Я бы не стал так категорично:
Так давайте попытаемся написать программу, которую проблематично будет создать на других языках.
Я вам назову дюжину языков на которых это можно написать, кстати не только не «проблематично» а много проще, красивее и что главное удобочитаемо
На лямбдах например на питоне, на чистых процедурах (или лямбдах) на тикле, да на том же javascript.

Кстати, кому интересно, набросал подобное на tcl, развернуть ...
Программа на tcl ...

proc program {args} {
  ## save current :
  set self [info body program]
  ## create each new code, evaluate it :
  foreach code $args {
    proc program {args} $code
    program {*}$args
  }
  ## restore self (if needed):
  ## proc program {args} $self
  ##TO_UNCOMMENT puts " !!!! it was a comment ..."
}

# draw code of program:
puts start------code-----[info body program]-----------\n
program {
  #1st program: i=1, print somthing with i
  set i 1
  puts "Program No. $i\n"
} {
  #2nd program: i=2, print somthing with i, modify self again - print current body, execute and modify again :
  set i 2
  puts "Now No. $i"
  # expand previous self :
  set code [string map {{##TO_UNCOMMENT } {}} [uplevel set self]]
  append code {
  puts " !!!! added code ..."
  }
  proc program {args} $code
  puts ...------code-----[info body program]-----------\n
  program {
    #third program: i=3, print somthing with i
    set i 3
    puts "No. equal $i\n"
  }
}
# draw last code of program:
puts \nend------code-----[info body program]-----------\n

Результат исполнения:

start------code-----
  ## save current :
  set self [info body program]
  ## create each new code, evaluate it :
  foreach code $args {
    proc program {args} $code
    program {*}$args
  }
  ## restore self (if needed):
  ## proc program {args} $self
  ##TO_UNCOMMENT puts " !!!! it was a comment ..."
-----------

Program No. 1

Now No. 2
...------code-----
  ## save current :
  set self [info body program]
  ## create each new code, evaluate it :
  foreach code $args {
    proc program {args} $code
    program {*}$args
  }
  ## restore self (if needed):
  ## proc program {args} $self
  puts " !!!! it was a comment ..."

  puts " !!!! added code ..."
  -----------

No. equal 3

 !!!! it was a comment ...
 !!!! added code ...

end------code-----
    #third program: i=3, print somthing with i
    set i 3
    puts "No. equal $i\n"
  -----------

И это все jit компилится в байт код.

А добавив функцию save, пользовательский ввод и загружая это через "source 1.txt" получим тоже что и у вас.


А вообще такие вещи используются как правило для расщирения примитивов… (например написать собственный «while» или «foreach» работающие в текущем scope подпрограммы).

Lisp я тоже уважаю, но конкретно вашу задачу, я бы делал на чем-то другом…
Слабо знаком с tcl, честно говоря, поэтому ваш пример для меня тяжело воспринимается. Постараюсь разобраться на досуге.
Почему Lisp? Тут у нас возникает задача, когда нужно собрать рекурсивно код для исполнения из мельчайших элементов, взятых из БД по определенным критериям. Тут очень спасает простота синтаксиса Лиспа: фактически одни скобки. Рассматривал вариант на Lua и на JS, мне они показались значительно менее пригодными.
Заранее прошу прощения за офтоп.

Никак не пойму, как количесво проголосовавших за статью может быть дробным? например «всего 28: 25.5 + и 2.5 -»?
read-only голоса считаются по 0.5 скорее всего.
Рид онли — это рид онли. +-0.5 это просмотр результата без голосования.
тогда в каком случае прибавляются, а в каком вычитаются эти 0.5 при просмотре без голосования?
Одновременно. Бал делится и туды и сюды.
Второй блок кода (начинается с "(defun start-program ()") совершенно нечитаем. Наверное, его надо по-другому отформатировать, — сейчас создается ощущение, что кто-то в перловом однострочнике наставил переводов строк. :)
Увидел две страшных неправды в тексте, которые отбили всё желание читать:

  1. «SBCL — интерпретатор»
  2. Форматирование!!!111


Правда заключается в следющем:

  1. SBCL — компилятор! Ком-пи-ля-тор. В машинные коды!
  2. Лисп так форматировать нельзя!!!11 Нель-зя!!1 Закрывающие скобки нужно оставлять на той же строчке!!11


Можно было хотя бы основы то выучить, прежде чем писать статью на популярный ресурс?

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

Форматирование я намеренно оставил свое.
(я
    (знаю
         (что
              (принято
                   (так)))))

Но мне такой стиль не очень нравится. Раз язык позволяет (не Python же), то зачем вгонять себя в жесткие рамки, определенные кем-то другим. В моей системе форматирования есть свои правила и логика, это не хаос.

P.S. Я знаю, что вы спец в Лиспе, и рассчитывал от вас на более конструктивную критику.
Нет, SBCL НЕ интерпретатор. Еще раз. Это компилятор. (хотя в этой реализации CL и есть интерпретатор, он отключен по дефолту).

Функция интерпретатора в том чтобы выполнять программу по ходу разбора текста. А SBCL, даже в eval, сначала всё компилирует. Это компилятор и точка. Выполняются — машкоды, и процессором, а не самим SBCL.

Семантика лиспа, связанная с генерацией кода на лету, называется не интерпретация, а «метациклический вычислитель».

Я понимаю желание оправдаться, но не надо запутывать читателей.

Вышесказанное относится и к стилю кода.
Писать на лиспе, расставляя скобки как на Си — нельзя. Это очень плохой стиль. Это надо понять, и не надо вести себя как первокурсник, который противится форматированию кода лесенкой и пишет без отступов.

Язык может много что позволять, но это не значит, что это надо делать(Си, например, позволяет разыменовывать невалидные указатели и не освобождать динамическую память)
С интерпретатором получается нечестно, задача интересней выглядит для компилируемых языков. Сможет ли какой-нибудь компилятор лиспа переварить эту программу? Получается, что с собой надо таскать компилятор и специальный «загрузчик» который будет заменять обновленные части программы в dll/so модулях. Мало того, такая программа будет вынужденно open-source, так как в памяти придется держать все исходники.
Честно, когда писал свой коммент, коммента выше не было.
Насколько мне известно SBCL вот таскает за собой себя же целиком. При компиляции в исполняемый файл, последний даже для «helloworld» получается размером около 50 Мбайт, без компромиссов.

Часть исходников может загржуться из зашифрованных файлов и сразу компилироваться, не болтаясь в памяти в виде текстов/списков. Потом обфускация и все такое. JS на сайтах тоже фактически OpenSource, как не крути.

Да, конечно, совокупность факторов в нашем безумном денежном мире результирует в то, что эта технология не распостранена, но рано или поздно, ей суждено будет явиться, когда программы будут анализировать сами себя и корректировать свою работу без участия человека.
А шифрование и обусфикация может быть отключена (настроена), чтобы если я и так open-source, не получать замеделение из-за шифрования и выполнения обусфицированного кода?
Может читал невнимательно, но мне показалось, что пример для самоизменяющейся программы какой-то неудачный. По сути все завязано на eval, который есть в куче других языков. Хотелось бы что-то самобытное на лиспе посмотреть.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории