Вы путаете язык и реализацию (компилятор и т.д.). Один и тот же язык может иметь разные реализации. Даже если вместо процессора будет сидеть человек со счётами, на выразительности языка это никак не скажется.
а потому если можно рекурсию преобразовать к циклам так и поступают
Неверно. Если Вы читали книги, на которые ссылаетесь, то должны знать, что рекурсия может описывать как рекурсивные вычислительные процессы, так и итеративные.
«переменная» — это любое место, куда можно что‑то положить, и потом это оттуда же взять
То есть, глядя на код, нельзя сказать, сколько там определено переменных? А HelloWorld содержит как минимум 16 переменных (основные регистры)? Даже не 16, а .... (добавить стек и т.п.).
с точки зрения обсустройства памяти различия небольшие (или их вовсе нет)
Отличия принципиальные. В функциональных языках переменная - это именованное выражение. В какие моменты и сколько раз оно будет вычисляться, решает компилятор.
ваш Хацкель совершенно ни при чём, даже если в нём есть похожий паттерн, это не означает, что именно он является прототипом
Этот паттерн изначально появился в виде "выражения вычисления" (способ задавать монады) в функциональном F#. Через 4 года его добавили в C#. Читайте историю.
пайп функций это именно функциональный стиль. Он описан ещё в старых книжках по Лиспу Хювенена/Сапеняна в 1985-88 годах (ЕМНИП).
Вы ссылаетесь на Лисп без указания контекста, как будто всё, что есть в Лиспе (включая defvar), это "функциональщина".
Я привёл код на F#. Вы привели описание на пёрл/пхп. Судя по описанию, Ваш код длиннее. А судя по упоминанию регулярных выражений, при небольшом усложнении задачи (например, "вернуть тип, который встречается в таблице чаще") Ваш код значительно усложняется.
Можно и нужно, если мы говорим о языке программирования, а не о языке вместе с "окружением" (библиотеки, сообщество, эффективность компилятора и т.п.).
У Вас же получается, что один и тот же язык то хороший, то плохой.
соответственно из-за этого хвостовые рекурсии "оптимизируют" обратно в процедурный стиль
Не имеет значения, во что это превратит компилятор. Более того, компилятор может транслировать функциональный код в код на Си (да-да, с циклами), чтобы получить возможность использовать сторонний компилятор для языка Си, имеющий 100500 оптимизаций для десятков различных процессоров.
выделенным местом, куда можно что‑то положить и потом оттуда его брать
"Выделенным" означает, что у этого места есть (заданное в коде) имя.
var y = x * 10 + 5;
Вероятно, компилятор выделит место для хранения промежуточного результата вычислений (10*x), но это место не будет переменной, так как у него нет имени. Более того, разговор об этом лишён смысла без указания конкретных процессора и компилятора.
Например, если в процессоре нет инструкции Mul , то для хранения промежуточных результатов потребуется не одна, а несколько ячеек памяти (вычисление умножения через сложение). А если в процессоре есть инструкция AddMull, то промежуточных результатов вообще не будет.
Почему? потому что он более сложен к изложению чего-либо отличного от злосчастных никому не нужных рядов Фибоначчи.
Вы продолжаете повторять это необоснованное утверждение, игнорируя факты. Например, то, что моя программа на F# оказалась проще и короче, чем Ваша на php/perl.
«Отсутствие переменных» — это от Вашего непонимания, как оно работает под капотом.
Я прекрасно понимаю, как это работает под капотом. Но мы говорим о языках программирования, а не об архитектуре процессора. С таким же успехом можно заявить, что в ООП нет классов, так как в архитектуре процессора нет инструкции "создать экземпляр класса".
Дискуссия идёт о языках программирования, а не об их конкретных реализациях.
параметр return() — это тоже неявная [регистровая] переменная
параметр функции — это [регистровая] переменная, просто она не объявляется явно
Это неверно. Либо Вы вкладываете в термин "переменная" нетрадиционный смысл.
Вы путаете язык и реализацию (компилятор и т.д.). Один и тот же язык может иметь разные реализации. Даже если вместо процессора будет сидеть человек со счётами, на выразительности языка это никак не скажется.
Неверно.
Не вижу никакой разницы по производительности между циклом и хвостовым вызовом.
В каких?
Когда в перечисленных Вами языках когда async/await появился?
Неверно. Если Вы читали книги, на которые ссылаетесь, то должны знать, что рекурсия может описывать как рекурсивные вычислительные процессы, так и итеративные.
То есть, глядя на код, нельзя сказать, сколько там определено переменных? А HelloWorld содержит как минимум 16 переменных (основные регистры)? Даже не 16, а .... (добавить стек и т.п.).
Отличия принципиальные. В функциональных языках переменная - это именованное выражение. В какие моменты и сколько раз оно будет вычисляться, решает компилятор.
Этот паттерн изначально появился в виде "выражения вычисления" (способ задавать монады) в функциональном F#. Через 4 года его добавили в C#. Читайте историю.
Вы ссылаетесь на Лисп без указания контекста, как будто всё, что есть в Лиспе (включая defvar), это "функциональщина".
Мы не соревновались, мы сравнивали.
Я привёл код на F#. Вы привели описание на пёрл/пхп. Судя по описанию, Ваш код длиннее. А судя по упоминанию регулярных выражений, при небольшом усложнении задачи (например, "вернуть тип, который встречается в таблице чаще") Ваш код значительно усложняется.
Мой шаг обладает общностью. Ваш шаг перестаёт работать, когда в программе встречается ветвление.
Тот, кто паял схемы на диодах, не может ошибаться?
Можно и нужно, если мы говорим о языке программирования, а не о языке вместе с "окружением" (библиотеки, сообщество, эффективность компилятора и т.п.).
У Вас же получается, что один и тот же язык то хороший, то плохой.
Не имеет значения, во что это превратит компилятор. Более того, компилятор может транслировать функциональный код в код на Си (да-да, с циклами), чтобы получить возможность использовать сторонний компилятор для языка Си, имеющий 100500 оптимизаций для десятков различных процессоров.
Из того, что Вы придумали, как переписать этот код в виде конвейера, не следует, что в коде конвейер.
Аналогично, если я придумаю, как переписать цикл в Вашем коде через рекурсию, не следует, что в Вашем коде рекурсия.
"Выделенным" означает, что у этого места есть (заданное в коде) имя.
Вероятно, компилятор выделит место для хранения промежуточного результата вычислений (10*x), но это место не будет переменной, так как у него нет имени. Более того, разговор об этом лишён смысла без указания конкретных процессора и компилятора.
Например, если в процессоре нет инструкции Mul , то для хранения промежуточных результатов потребуется не одна, а несколько ячеек памяти (вычисление умножения через сложение). А если в процессоре есть инструкция AddMull, то промежуточных результатов вообще не будет.
Сначала нужно договориться о значении термина "переменная". В императивном программировании переменная - это именованная память.
Вы продолжаете повторять это необоснованное утверждение, игнорируя факты. Например, то, что моя программа на F# оказалась проще и короче, чем Ваша на php/perl.
Я прекрасно понимаю, как это работает под капотом. Но мы говорим о языках программирования, а не об архитектуре процессора. С таким же успехом можно заявить, что в ООП нет классов, так как в архитектуре процессора нет инструкции "создать экземпляр класса".
Дискуссия идёт о языках программирования, а не об их конкретных реализациях.
Это неверно. Либо Вы вкладываете в термин "переменная" нетрадиционный смысл.
Отсутствие переменных возможно в любой чистой функции.
Этот код можно переписать без объявления переменной:
В лиспе есть "настоящие" переменные (defvar) и синтаксический сахар (let). Любую let переменную можно заменить на вызов функции.
Go мультипарадигменный, а не «процедурный». Позволяет использовать ООП и ФП.
Вы написали, что функциональный стиль - это цепочки функций. На самом деле это ортогональные понятия.
Накладные расходы на "вызов функции" зависят от того, во что это превратит компилятор. Вызов функции может вообще ничего не стоить.