LINUX.ORG.RU
ФорумTalks

Зачем нужна обработка списков в искусственном интеллекте?

 ,


2

6

По легенде, Lisp придуман для решения задач искусственного интеллекта. Зачем для этого надо было придумывать такой довольно необычный язык - язык обработки списков? Чем например Fortran не подошёл бы?

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

Фигасе какой лисп могучий. У него только одно правило — никаких правил?

скорее наоборот. LISP намного проще.

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

не. Я уже говорил — это синтаксический сахар. Тут используются временные переменные. Т.е. аргументами * являются две временных переменных. А в LISP'е — два выражения. Естественно, здесь результат одинаковый получается. В коде и там и там просто 8 записано.

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

В сишечке тебе сначала надо ВЫПОЛНИТЬ функцию, и полученный результат подставить в код.

Лол, прям как лиспе.

Т.е. для вычисления factorial(2) ты сначала вычисляешь factorial(1), и получаешь _результат_ равный 1

Объясни мне, пожалуйста, чем это отличается от лиспа:

* (defun fact (n) (if (zerop n) 1 (* n (fact (1- n)))))

FACT
* (trace fact)

(FACT)
* (fact 5)
  0: (FACT 5)
    1: (FACT 4)
      2: (FACT 3)
        3: (FACT 2)
          4: (FACT 1)
            5: (FACT 0)
            5: FACT returned 1
          4: FACT returned 1
        3: FACT returned 2
      2: FACT returned 6
    1: FACT returned 24
  0: FACT returned 120
120
* 

?

Если тебе не хватает мозгов, я могу разжевать. Для вычисления (fact 5) мы сначала вычисляем (fact 4), для которого, в свою очередь, вычисляем (fact 3), и так до тех пор, пока (fact 0) не вернёт единицу, которую мы подставляем в формулу 1 * 1, получаем единицу, которую подставляем в формулу 2 * 1, получаем двойку, которую подставляем в формулу 3 * 2… В конечном итоге мы получаем формулу 5 * 24, что в результате нам даёт 120.

Прям как в сишечке.

А вот в CL ты в качестве результата factorial(2) получаешь _константу_ 1. Разница между «числом» и «константой» тебе понятна?

Нет, всё-таки ты тупой.

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

Ты понимаешь что результат вычисления будет разным в зависимости от порядка вычисления?

ну и что?

То что назвать порядок вычисления деталью реализации - тяжелая наркомания.

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

А напиши на фортране программу, которая на входе получает функцию, а на выходе --- её производную.

если функция == многочлен степени N, то можно задать её массивом в N коэффициентов. И посчитать эти коэффициенты для производной. Ничего сложного. Сложности начинаются тогда, когда функция — не многочлен.

emulek
()

Чем например Fortran не подошёл бы?

По легенде на фортране можно написать вообще ВСЁ, поэтому подошел бы.

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

В чистом ФП так и будет. Это, конечно, не очень интересно...

— Дважды два — четыре.
— Нет, дважды два — это два умножить на два, два во второй степени. И это будет четыре. Это, конечно, не очень интересно...
— ?!

i-rinat ★★★★★
()
Ответ на: комментарий от theNamelessOne

а про то, что разворачивание функций происходит ПЕРЕД вычислением, а не во время, как в сишечке.

Невозможно в языке с аппликативным порядком (макросы откинем в сторону). Лисп в этом отношении от сишечки ничем не отличается.

это если ограничиться только функциями. А есть в сишечке ещё и операторы. Например это твой

1 ? 0 : 1/0;
в сишечке выполнится БЕЗ ошибок. Если 1/0 это какое-то a/b, где b не константа, а переменная. Если-же взять не оператор, а функцию, то да, там сначала будут вычисления, даже если функция эти значения не использует.

Но я говорил совсем не про это.

Внезапно, if в Scheme/CL ленивый (т.к. это на самом деле специальная форма), а ты опять обосрался. И «ошибки в этом моём коде» именно происходят из-за того, что аргументы вычисляются перед непосредственным вызовом функции, что можно посмотреть на следующем примере:

это не «вызов» функции, а вычисление её значения. В этот момент компилятор уже знает, что это 1/0, и начинает вычислять данное выражение (которое вычислять не нужно, ибо от него ничего не зависит). Но в этот момент уже ВСЁ распарсено и развёрнуто. Осталось подставить числа и посчитать. Вот твой компилятор не осилил это сделать. Никто не гарантировал, что компилятор не будет считать ту часть функции, которая не влияет на результат. Да, он _может_ её не считать. Но не обязан.

В сишечке принципиально невозможно не считать аргументы в неинлайновых функциях, ибо их надо как-то загрузить в память. И не важно, влияют они, или нет. Но если этот оператор не функция, то можно и не считать. Компилятор C/C++ и не считает, для операторов &&, ||, и тернарного оператора.

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

Мне не понятна. Можно объяснить, в чем она?

x*1 === x

Это если 1 — константа. Если это переменная, вот такая:

int foo()
{  return 1; }
int x = foo();

то компилятор сможет узнать в x константу лишь если развернёт foo(). В смысле сделает её inline. И если она в том же модуле.

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

В этот момент компилятор уже знает, что это 1/0, и начинает вычислять данное выражение (которое вычислять не нужно, ибо от него ничего не зависит). Но в этот момент уже ВСЁ распарсено и развёрнуто. Осталось подставить числа и посчитать. Вот твой компилятор не осилил это сделать. Никто не гарантировал, что компилятор не будет считать ту часть функции, которая не влияет на результат. Да, он _может_ её не считать. Но не обязан.

Слушай, а ты умеешь программировать? Не рассуждать о программировании, а программировать?

i-rinat ★★★★★
()
Ответ на: комментарий от theNamelessOne

Прям как в сишечке.

IRL в сишечке она соптимизирует, и сделает таки цикл. AFAIK в лиспе (должно быть)также.

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

То, что был приведен совсем уж плохой пример.

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

Например это твой 1 ? 0 : 1/0; в сишечке выполнится БЕЗ ошибок. Если 1/0 это какое-то a/b, где b не константа, а переменная.

Это должно мне что-то говорить или как-то доказывать твою точку зрения?

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

Как и в лиспе.

это не «вызов» функции, а вычисление её значения.

Нет, ты опять бредишь, это именно вызов функции. Наиболее близким аналогом вычисления значения функции в CL является следующее #'+

Никто не гарантировал, что компилятор не будет считать ту часть функции, которая не влияет на результат. Да, он _может_ её не считать. Но не обязан.

Меня поражает, как ты можешь так нагло врать, если я уже не раз давал пруфы обратного в этом треде. Или ты английский не понимаешь? Ещё раз повторюсь: все реализации Scheme/CL обязаны вычислять все аргументы функций перед вызовом функции. Т.е. это языки с аппликативным порядком. И нет никакого «не обязан». Это строго определено стандартами.

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

Но если этот оператор не функция, то можно и не считать.

Покажи мне реализацию C, которая может не считать аргументы оператора +. Только не говори, что это не оператор, а функция.

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

Это в данном случае не имеет значения. Возьми вместо константы переменную, значение которой определено в рантайме:

* (fact (read))
3
  0: (FACT 3)
    1: (FACT 2)
      2: (FACT 1)
        3: (FACT 0)
        3: FACT returned 1
      2: FACT returned 1
    1: FACT returned 2
  0: FACT returned 6
6
* 
theNamelessOne ★★★★★
()
Ответ на: комментарий от emulek

Сложности начинаются тогда, когда функция — не многочлен.

Нет. Сложности начинаются тогда, когда выбирают фортран для символьного дифферинциирования.

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

Что-то ты всё таки путаешь. Практически во всех лиспах (foo a) эквивалентно сишному foo(a), просто скобки переставили. То что ты назвал в начале срача «передача функции, а не числа» в ленивом языке называется thunk. Лиспы в общем случае не ленивые.

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

Это должно мне что-то говорить или как-то доказывать твою точку зрения?

это доказывает, что в сишечке операторы != функции. Т.ч. как будет вычисляться, и будет-ли вообще вычисляться x+y — неизвестно в общем случае.

Наиболее близким аналогом вычисления значения функции в CL является следующее #'+

«значение функции» == результат выполнения этой функции. Для + это сумма.

Ещё раз повторюсь: все реализации Scheme/CL обязаны вычислять все аргументы функций перед вызовом функции. Т.е. это языки с аппликативным порядком. И нет никакого «не обязан». Это строго определено стандартами.

ты читать умеешь? Для _функции_ это так, но не для оператора в C/C++. Да и вообще не так везде, где есть ленивые операторы. По твоей ссылке об этом написано: «Evaluating (try 0 (/ 1 0)) generates an error in Scheme. With lazy evaluation, there would be no error. Evaluating the expression would return 1, because the argument (/ 1 0) would never be evaluated.» Так вот часть операторов в сишечке ленивые, а остальные — неопределённые. Может будут выполняться, а может и не будут. И если будут, то непонятно в каком порядке. Как карта ляжет. А как оно в схеме — я без понятия. Тебе виднее, раз это для тебя так важно. Я вообще про другое говорил, однако ты просто ничего не понял, и стал мне тут доказывать нечто, что к теме нашего спора и не имеет отношения.

Покажи мне реализацию C, которая может не считать аргументы оператора +. Только не говори, что это не оператор, а функция.

gcc из кода x-- - --x делает константный ноль. Да, вот прямо так и пишет 0 в нужную ячейку памяти. И ему пофиг, чему равен этот x.

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

Это в данном случае не имеет значения. Возьми вместо константы переменную, значение которой определено в рантайме:

ты лучше напиши программу, которая проверяет переполнение. И если переполнение есть, спрашивает: «считать дальше? да/нет?». Ну и в зависимости от ответа либо считает, лили не считает. Тогда и поймёшь, что имеет значение, а что — не имеет.

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

Сложности начинаются тогда, когда функция — не многочлен.

Нет. Сложности начинаются тогда, когда выбирают фортран для символьного дифферинциирования.

да. Если многочлен, то задача решается численно. Без всяких символов.

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

gcc из кода x-- - --x делает константный ноль. Да, вот прямо так и пишет 0 в нужную ячейку памяти. И ему пофиг, чему равен этот x.

Это особенности реализации компилятора, clang вот считает.

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

Практически во всех лиспах (foo a) эквивалентно сишному foo(a)

безусловно так.

То что ты назвал в начале срача «передача функции, а не числа» в ленивом языке называется thunk.

я знаю.

Я говорил о том, что в сишечке тривиальные операторы работают совершенно не так, как функции. А вот в ФП — именно так, и никак иначе(исключая конечно особые формы).

Лиспы в общем случае не ленивые.

сишечка с т.з. аргументов функций тоже не ленивая.

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

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

Твой гцц может соптимизить-аут куски вычислений, но только тогда, когда это будет прозрачно-незаметно. Случаи с побочными эффектами будут вычислены в любом случае (в т.ч. и 1/x - 1/x).

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

Слушай, а ты умеешь программировать? Не рассуждать о программировании, а программировать?

АПВС?

Потому что я в этом сомневаюсь.

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

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

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

Это особенности реализации компилятора

просили «показать хоть одну». Я показал.

Интереснее без оптимизации, тогда gcc вычисляет этот код СРАЗУ, в два потока на двух ALU, если речь про x86 не старше iPentium. Не, я не упоролся, это к создателям gcc вопросы.

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

Я говорил о том,

Ты говорил, что факториал разворачивается в цепь отложенных вычислений и вычисляется только при редукции, между прочим ;)

что в сишечке тривиальные операторы работают совершенно не так, как функции

The order of evaluation of arguments is unspecified.

Как и порядок вычисления операндов (в т.ч. и составных). В общем-то cse работает и для аргументов — если нет побочки, то в foo(a+1, a+1) будет только одно вычисление. Где совершенная разница между оператором и функцией?

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

Это не корректная операция

с этим никто и не спорил.

и по уму должна быть забанена

мы тут про ИИ, который сам свой алгоритм строит.

Хотя кому это может понадобиться — непонятно

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

А ещё лучше, это если ЯП такой гадости не может допустить.

Твой гцц может соптимизить-аут куски вычислений, но только тогда, когда это будет прозрачно-незаметно. Случаи с побочными эффектами будут вычислены в любом случае (в т.ч. и 1/x - 1/x).

доказать сможешь?

emulek
()
Ответ на: комментарий от i-rinat

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

когда я на ЛОРе, я не пишу код; когда я пишу код, я не на ЛОРе. Разве это не очевидно?

А застрял скорее всего ты — изучил какую-то часть ЯП, а то, что не понял, тупо вызубрил. Ну например тупо запомнил, что зависимые выражения нельзя между двумя соседними точками следования ставить. А почему, и главное ЗАЧЕМ так сделали — тебя не волнует.

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

emulek
()
Ответ на: комментарий от emulek
int
foo(int x)
{
    return 1/x - 1/x;
}

int
bar(int x)
{
    if (x != 0)
        return 1/x - 1/x;
    else
        return 0;
}

gcc -O3 --save-temps -c y.c

_foo:
        pushq   %rbp
        movq    %rsp, %rbp
        xorl    %eax, %eax
        popq    %rbp
        ret

_bar:
        pushq   %rbp
        movq    %rsp, %rbp
        xorl    %eax, %eax
        popq    %rbp
        ret

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

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

Ты говорил, что факториал разворачивается в цепь отложенных вычислений и вычисляется только при редукции, между прочим ;)

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

The order of evaluation of arguments is unspecified.

во всяком случае, мы знаем, что они _будут_ вычислены, и знаем _когда_. Только абсолютного порядка не знаем. Для операторов всё совсем печально: мы не знаем будет-ли вообще вычислен аргумент? А если будет, то не знаем когда (в выражении (x+y)*(z+t) неопределён не только порядок x,y и z,t, но и порядок x относительно t)

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

АПВС?

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

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

«значение функции» == результат выполнения этой функции.

Ок, а в чём тогда заключается отличие вызова функции от вычисления её значения, о котором ты говорил в этом сообщении? Ты прямым текстом сказал, что вычисление значение функции не является вызовом функции. Или ты уже не помнишь, что писал полчаса назад?

И не ты ли говорил, что в Lisp'е функций нет, как и их значений? Просьба не лукавить, я при необходимости цитату найду.

И почему ты вообще нёс бред типа:

b = getRandom(); c = b * getRandom();


тут ты умножаешь ЗНАЧЕНИЕ функции, а не функцию.

?

Ладно, идём дальше.

Для _функции_ это так, но не для оператора в C/C++

Ты издеваешься? В посте, на который я отвечал, ты говорил именно про функции, и не C/C++, а Lisp:

Никто не гарантировал, что компилятор не будет считать ту часть функции, которая не влияет на результат. Да, он _может_ её не считать. Но не обязан.

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

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

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

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

Что доказывает, что либо гцц и шланг неправильно кодогенерят, либо ты таки прав

я как раз и говорил о том, что компилятор может НЕ вычислять какие-то части выражения, если от них не зависит результат. 1/x-1/x равно 0 в любом случае. Компилятор ничего НЕ считает. О чём я и говорил

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

Ты прямым текстом сказал, что вычисление значение функции не является вызовом функции.

так и есть. Причём вне зависимости от ЯП. Вон выше пример функции, которая ничего не считает, а всегда тупо отдаёт ноль.

И не ты ли говорил, что в Lisp'е функций нет, как и их значений? Просьба не лукавить, я при необходимости цитату найду.

найди. Это твоим проблемы, что ты невнимательно читаешь.

b = getRandom(); c = b * getRandom();

тут ты умножаешь ЗНАЧЕНИЕ функции, а не функцию.

?

функция == список действий. Aka алгоритм. «Значение функции» == некоторая информация, являющаяся результатом работы этой функции. Что тебе непонятно?

В посте, на который я отвечал, ты говорил именно про функции, и не C/C++, а Lisp:

я говорил в общем. В общем случае — не обязан считать то, что на результат не влияет. И не считает, ЧСХ. Мало того, если это gcc, то не просто «может», а ещё и старается именно так делать. Быдлокод x-- - --x можно вычислить тремя разными способами, и gcc выбирает именно тот, когда считать НЕ нужно.

Но это всё частности, я говорил про другое. А именно про то, что код в ФП _вычисляется_ весь и сразу, а не по частям, как в сишечке. Потому неоднозначности не только нет, но и быть не может.

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

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

Ну что я могу поделать, если ты почти в каждом сообщении (например, как вычисляется рекурсивный факториал) несёшь бред?

это ты несёшь бред, если думаешь, что ФУНКЦИЮ можно на что-то умножить, и получить число. Нет, ты получишь другую функцию, которая является функцией умножения от данной функции и ещё чего-то. Т.е. «вычисление» n! даёт «ответ» n*(n-1)*...*1, а вовсе не значение факториала. Значение будет вычислено уже после.

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

что стыдно за остальных лисперов становится. Не делай так больше.

иди, разупорись. Я не лиспер ни разу.

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

это ты несёшь бред, если думаешь, что ФУНКЦИЮ можно на что-то умножить, и получить число. Нет, ты получишь другую функцию, которая является функцией умножения от данной функции и ещё чего-то.

Твоя цитата:

суть в том, что в последней строке ты умножаешь число на функцию.

Бгг, то есть ты имел в виду, что в вызове (* n (factorial (- n 1))) мы умножаем число n на функцию factorial и получаем другую функцию?

Т.е. «вычисление» n! даёт «ответ» n*(n-1)*...*1, а вовсе не значение факториала. Значение будет вычислено уже после.

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

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

когда я на ЛОРе, я не пишу код; когда я пишу код, я не на ЛОРе.

То есть ты вообще не пишешь код. Вот визуализация твоего флуда: http://img132.imageshack.us/img132/9808/m55c.png

В некоторые дни ты флудишь столько, что едва времени на сон остаётся.

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

В некоторые дни ты флудишь столько, что едва времени на сон остаётся.

Как Страшно Жить... Может меня много?

То есть ты вообще не пишешь код. Вот визуализация твоего флуда: http://img132.imageshack.us/img132/9808/m55c.png

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

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

Бгг, то есть ты имел в виду, что в вызове (* n (factorial (- n 1))) мы умножаем число n на функцию factorial и получаем другую функцию?

ну вот, видишь — ты начинаешь понимать... Действительно, (* n (factorial (- n 1))) — новая функция. Сконструированная из старой. Такие очень часто встречаются в численных методах, называются «рекуррентные» (в данном случае функция сконструирована с помощью самой себя).

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

ты меня ничем не задел. Потом поймёшь. Ещё раз повторю: тот порядок, о котором ты тут так много говорил, меня абсолютно не волнует в данном случае. Речь о совсем другом порядке. О порядке конструирования функций. А сами вычисления меня мало волнуют. Мне интересно знать, как из 3! получается 3*2*1, а вопрос _вычисления_ этого выражения, кажется мне банальным и очевидным. Неужто для тебя не так?

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

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

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

Я давно подозревал, что ты — марковский процесс первого порядка. :)

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

сделай аналогичную картинку и покажи свой чудо-стайл.

сделаю. Кода нечего будет делать.

Я давно подозревал, что ты — марковский процесс первого порядка. :)

типа того, да (:

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

Кода нечего будет делать.

Будто тебе сейчас есть что делать.

i-rinat ★★★★★
()
Ответ на: комментарий от emulek

ну вот, видишь — ты начинаешь понимать... Действительно, (* n (factorial (- n 1))) — новая функция. Сконструированная из старой.

Я тут влезу в ваш спор, потому как осилил лишь половину треда. Объясню на пальцах (хоть лисп не знаю): код на си, который был в начале, аля классическая рекурсия свалится через н-десять циклов, ИБО стек процессора будет забит, в простонародье stack overflow. В отличие от такого описания, код на лиспе делает туже рекурсию, но не на стеке! Ибо никто бы не стал изобретать такой язык :) Аналог на Си делается через создание новой ссылки на функцию и все вычисления производятся в куче. Это один из вариантов. Другой вариант: выход из функции с подсчитанным промежуточным значением и далее снова вход в рекурсию, аля с помощью защищенного счетчика внутри функции факториала.

Что касается вычисления x*y*z это можно узнать через вывод асмоского кода и зависит от компилятора. Точно также в зависимости от лисповского/хаскельского интерпретатора/компилятор выходной код будет разным.

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

Объясню на пальцах (хоть лисп не знаю): код на си, который был в начале, аля классическая рекурсия свалится через н-десять циклов, ИБО стек процессора будет забит, в простонародье stack overflow. В отличие от такого описания, код на лиспе делает туже рекурсию, но не на стеке! Ибо никто бы не стал изобретать такой язык :) Аналог на Си делается через создание новой ссылки на функцию и все вычисления производятся в куче. Это один из вариантов. Другой вариант: выход из функции с подсчитанным промежуточным значением и далее снова вход в рекурсию, аля с помощью защищенного счетчика внутри функции факториала.

эти варианты отличаются тем, что они как минимум на порядок дороже варианта со стеком. Это в C. В LISP'е это получается как не странно даже несколько дешевл, чем в сишечке, потому-что всё уже реализовано 40 лет назад, и не нужно каждый раз изобретать этот велосипед.

И да, вариант со стеком тоже годен. Полно задач, которые сходятся пропорционально логарифму. Логарифм растёт медленно, и стека вполне хватает. К примеру, только упоротый может делать на куче ту же qsort.

Что касается вычисления x*y*z это можно узнать через вывод асмоского кода и зависит от компилятора

не только. Один и тот же компилятор может давать(и даёт ЧСХ) разные варианты выполнения данного быдлокода

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

именно так. Ты же можешь придумать новый алгоритм? А твой код на сишечке — не может. Даже если и может, то он не сможет этот алгоритм выполнить, если конечно ты не реализуешь какое-то подмножество LISP'а в своём коде.

что мешает сишной программе сгенерировать текст программы на С, вызвать компилятор, а скомпилированный код динамически слинковать и вызвать? :)

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

А perl просто используется. Без фанатизма.

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

что мешает сишной программе сгенерировать текст программы на С, вызвать компилятор, а скомпилированный код динамически слинковать и вызвать? :)

ничего не мешает. Просто это очень дорого и никак не упрощаемо.

ну вот если ты будешь использовать ФП, то получив 3!=3*2*1 ты с лёгкостью выпилишь *1, которое тут не нужно.

  int factorial(int n) {
      return !n ? 1 : n * factorial(n - 1);
  }
тут тоже есть эта *1, как её отсюда выпилить? Такого рода оптимизации сплошь и рядом встречаются в автоматически сгенерированном коде, и надо их уметь делать, иначе даже простая задачка с формулой у тебя расползётся на сотни мб.

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