LINUX.ORG.RU

Конструкции языка против функций


0

0

Никто не задумывался, почему в некоторых языках программирования некоторые встроенные функции (например, print в python и php) представлены в виде особых языковых конструкций, а не функций, хотя могут быть вызваны и с тем же синтаксисом, что и функции?

Дабы постебаться над Оккама.

wfrr ★★☆
()

> почему в некоторых языках программирования некоторые встроенные функции (например, print в python и php) представлены в виде особых языковых конструкций, а не функций

Функция - это тоже конструкция языка.
В php наоборот - особая языковая конструкция представлена в виде функции.
Пятон не имеет смысла.

anonymous
()

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

DonkeyHot ★★★★★
()

Синтаксический сахар. Для удобства пользователя исключительно.

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

> Нет. В Lisp это специальная форма.

Значит плохо лисп помню. Но в smalltalk это точно так :).

anonymous
()

Особый синтаксис для фундаментальных конструкций типа for или if часто облегчает чтение кода и потому вполне уместен. При всех достоинствах Лиспа, читать его труднее, чем Питон. Алсо, надо заметить, что в Питоне от многих синтаксических излишеств типа операторов print и exec избавляются, и это хорошо, это правильно. У Гвидо неплохо получается сохранять равновесие между чистотой и практичностью, за то его и любим.

Вот в Руби наоборот тащат всё новый и новый синтаксис => сами себе могилу роют.

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

> При всех достоинствах Лиспа, читать его труднее, чем Питон.

Чушь. Питон слишком низкоуровневый, так что чтобы прочитать то же, читать надо гораздо больше. В один экран кода не влезет сложная концепция - и всё, читать уже очень непросто. На Лиспе же мало какая атомарная сущность может не влезть в один экран.

> Алсо

За такое надо убивать.

anonymous
()

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

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

>в Питоне от многих синтаксических излишеств типа операторов print и exec избавляются

Странные люди, эти аф^Wдатчане. Могли бы унифицировать 1 штуку(что-то типа "имя параметр ..." для вызова ф-й) и получить однородный и почти "расширяемый" синтаксис. А так - одна конструкция в ногу, остальные - не в ногу, меняем те, которых больше.

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

> троль детектед

Вот за это быдловскую фразу тоже предлагаю убивать, как и за идиотичное "алсо".

> чушь с первого и до последнего слова. ни питона ни лиспа не видел

А обосновать способен, жалкий мразёныш?

Запиши мне на Питоне, например, синтаксис простого калькулятора. В BNF. На Лиспе это и будет чистый BNF, в 10 строк максимум вместе с интерпретатором. На пистоне - полтора экрана нечитабельной параши, поскольку нормального метапрограммирования в быдлопистоне нет, и лямбы там угрёбищные.

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

Мне вот интересно было всегда, почему так получается: монстры лиспа, такие как Норвиг и Грэхем Питон уважают и хвалят, а анонимусы ЛОРа -- уверенно ругают.

Почему так?

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

Потому что пистон, при всех своих недостатках, горадо лучше всей прочей попсы (Java и тому подобные). И с пистона людей перетащить на что-то более серьёзное куда как проще чем с жабы. Вот и хвалим.

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

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

Псевдоэлитарное быдло детектед.

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

> У Гвидо неплохо получается сохранять равновесие между чистотой и практичностью, за то его и любим.

Бугага!!! Два типа классов, четыре типа определения метода, два разных типа строк(из-за которых появляется зоопарк библиотек), плюс еще "ништяки" вроде торчащих наружу кишок и через жопу сделанные приватные методы и атрибуты. Да, вот уж неплохо Гвидо раздает. Скоро питон по количеству костылей обгонит плюсы.

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

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

Сначала на лиспе покажи в 15 строк, потом глядишь, и на пайтоне сбацаем. Для определённости, на Scheme R5RS, чтобы не читерил.

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

> Для определённости, на Scheme R5RS, чтобы не читерил.

Э нет, так не пойдёт. Меряемся не мощностью библиотек из коробки, а возможностями выражения в языке высокоуровневых абстракций. R5RS канает только при условии наличия define-macro, голый R5RS - отстой.

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

Так вот, Scheme, с одной писанной за вечер на коленке библиотечкой 
парсинга:

(define-parser (export expression)
  expression:
   (term/left "+" expression/right) -> (+ ,left ,right)
   (term/left "-" expression/right) -> (- ,left ,right)
   (term/t) -> t
  term:
   (factor/left "*" term/right) -> (* ,left ,right)
   (factor/left "/" term/right) -> (* ,left ,right)
   (factor/f) -> f
  factor:
   ("(" expression/e ")") -> e
   (p:str-number?/num) -> num
   )

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

(parse-with expression "(10+2*2)/2") -> 7

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

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

> R5RS канает только при условии наличия define-macro

Ну давай, приведи 10 строк с define-macro.

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

http://zestyparser.adamatlas.org/
пример с главной страницы

from ZestyParser import *

t_paren_expr = (RawToken('(') + Only(Defer(lambda:t_expr)) + RawToken(')'))
t_factor = (t_paren_expr | Token('([0-9]*\.[0-9]+|[0-9]+)', group=0, to=float))

t_term = Defer(lambda:t_term)
t_term = (
    ((t_factor + RawToken('*') + t_term) >> (lambda r: r[0] * r[2])) |
    ((t_factor + RawToken('/') + t_term) >> (lambda r: r[0] / r[2])) |
    t_factor
)

t_expr = Defer(lambda:t_expr)
t_expr = (
    ((t_term + Omit(RawToken('+')) + t_expr) >> sum) |
    ((t_term + RawToken('-') + t_expr) >> (lambda r: r[0] - r[2])) |
    t_term
)

def evaluate(expression):
    parser = ZestyParser(expression)
    return parser.scan(t_expr)

if __name__ == '__main__':
    from sys import stdin
    print evaluate(stdin.read())

Многие библиотеки ещё docstring-и используют для задания BNF-а.

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

> Scheme, с одной писанной за вечер на коленке библиотечкой парсинга

Ну то есть меряемся не "мощностью библиотек из коробки", а мощностью самописанных библиотек? Ну так всё, что в коробке, можно написать и самому.

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

>Гвидо голландец :)

Я так и знал :) - зачем-то полез проверять и был обманут многообразием названий этой нации. Впрочем, вряд ли он обидится, да и сути критики его решения это не меняет.

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

Ну, во первых, оно гораздо более громоздкое чем мой вариант, а во вторых, в моём случае генерится быстрый LR(1) автомат (а в следующей версии ещё и GLR научится), а zestyparser генерит recursive descent, что не есть хорошо для большинства случаев и требует грязных хаков на уровне самой грамматики для улучшения производительности.

Так что метапрограммирование рулит, а high order functions - не очень.

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

> Нужно метапрограммирование подтягивать :(

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

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

> > Нужно метапрограммирование подтягивать :(

> Да его Питону, увы, подтягивать и некуда.

В смысле мне нужно подтягивать. Я конечно теоретически представляю, как оно сделано в твоём варианте, но только теоретически.

При том, что я пытаюсь написать компилятор схемы.. Хех.

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

> Я конечно теоретически представляю, как оно сделано в твоём варианте, но только теоретически.

А там всё очень просто на самом деле. Эта конструкция разворачивается в (define expression (cons номер-первого-состояния (vector ...)), где этот самый vector содержит состояния и переходы автомата. Так что собственно метапрограммирования тут мало, вся сложная логика (которую я тупо содрал с поведения Antlr и немного почитав драконью книгу) прячется внутри макроса, в один проход раскрывается. Совсем не так страшно как если бы было много уровней.

Да, ещё один секрет - символы переводятся в строки и парсятся, так что синтаксис уже не совсем голые S-выражения. То есть, <символ>: - это начало нового node, <символ>/<символ> это узел или токен с именем (чтоб не мучаться с их нумерацией, как во всяких там Yacc-ах).

> При том, что я пытаюсь написать компилятор схемы.. Хех.

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

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

> А там всё очень просто на самом деле. Эта конструкция разворачивается в (define expression (cons номер-первого-состояния (vector ...)), где этот самый vector содержит состояния и переходы автомата.

Ну да, понятно.

> Да, ещё один секрет - символы переводятся в строки и парсятся, так что синтаксис уже не совсем голые S-выражения.

Это я понял.

> Как раз неплохая задача на метапрограммирование. Тот же схемский let должен быть макрой а не встроенной конструкцией, аналогично и do, и некоторые другие конструкции.

Вопрос сложный. Я сейчас примерно так и делаю, у меня из core выражений только lambda, set!, if и call/cc, потом планирую сделать полноценный препроцессор, который будет макросы раскрывать и на них написать уже всё, что нужно. С define ещё до конца не разобрался, какой то он хитрый. Вот только мне уже кажется, что многие вещи, хоть и выражаются через lambda, но правильней вводить их в ядро, чтобы быстрее работало. С другой стороны, может лучше порабоать над оптимизациями, чтобы он на этапе препроцессинга раскрывал в неоптимальный код, потом этот код автоматически оптимизировал, ну а потом уже байткод генерил.

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

> Ну да, понятно.

Да, я забыл, там ещё один define генерится - лексер (список токенов выводится из определения парсера + некоторое количество грязных спекуляций, таких как игнорирование whitespace-ов).

> Я сейчас примерно так и делаю, у меня из core выражений только lambda, set!, if и call/cc, потом планирую сделать полноценный препроцессор, который будет макросы раскрывать и на них написать уже всё, что нужно.

Ещё нужен элементарный let (чтоб в лямбды не преобразовывать), а так - да, остального достаточно.

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

> С define ещё до конца не разобрался, какой то он хитрый

???

Какие трудности? Можно ввести несколько низкоуровневых define-ов - для констант, функций и макр, и сам define сделать макрой, раскрывающейся в define-constant или define-function.

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

Чтоб быстрее работало, надо в ядро ввести метки и goto. Можно даже встраиваемый ассемблер ввести. И тогда что угодно, сколь угодно эффективно, можно будет делать чисто на макрах.

Правда, метки и goto плохо дружат с CPS-преобразованием, но и этот вопрос решаем в принципе.

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

> Какие трудности? 

Вообще не до конца понятно, зачем он нужен, если есть let.

(define x 1)
(define y 2)
нужно преобразовывать в 
(let ((x 1))
     (let ((y 2))
          ...))
или все define-ы собирать в начало функции, или ещё как то.
   

> Чтоб быстрее работало, надо в ядро ввести метки и goto.

Здесь не понял ничего. Метки в лиспе я не представляю..

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

А CPS-conversion-а у меня нет. Я почитал диссертацию создателя Chez Scheme, и в основном по ней делаю. http://www.cs.indiana.edu/~dyb/papers/3imp.pdf
Он там довольно убедительно аргументирует в пользу своего варианта реализации. Продолжения получаются тяжеловесными, зато обращение к переменным очень быстрое. И с памятью лучше.

Вкратце, там замыкания создают копию всех захватываемых переменных, к переменным, которые не read-only, доступ идёт через указатель.
А продолжение просто копирует весь стек.

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

> Вообще не до конца понятно, зачем он нужен, если есть let.

У него открытый глобальный scope. У let же локальный.

> нужно преобразовывать в ...

И как это использовать в repl? Никак. А repl и сплайсы необходимы для реализации полноценных макр. Так что требуется отдельная конструкция для top level определений. И, конечно же, отдельная конструкция для макроопределений.

> Здесь не понял ничего. Метки в лиспе я не представляю..

set! представляешь, а goto - нет? Пользователю доступа к ним можно и не давать, обойдётся. А вот в макрах использовать можно и нужно, я так делал ещё во времена muLisp. Тогда тот же let и do сделать проще гораздо.

> А CPS-conversion-а у меня нет.

Тем более ни разу не проблема использовать метки и goto.

> Вкратце, там замыкания создают копию всех захватываемых переменных, к переменным, которые не read-only, доступ идёт через указатель.

Это просто стандартный lambda lifting.

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

Ага, для того и нужна хитрая VM. Тоже метод. Собственно, я с этим подходом и не спорю, мне он симпатичнее чем CPS.

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

> У него открытый глобальный scope. У let же локальный.  

не всегда.

(lambda ()
  (define x 1)
  x)

вот это многообразие меня немного смущает.

> И как это использовать в repl? Никак.

Всё, понял. Я про repl всё время забываю.

> set! представляешь, а goto - нет? Пользователю доступа к ним можно и не давать, обойдётся. А вот в макрах использовать можно и нужно, я так делал ещё во времена muLisp. 
> Тогда тот же let и do сделать проще гораздо. 

А пример можно? Как то так что ли?
((lambda (i)
   :loop:
   (display i)
   (set! i (+ i 1))
   (if (< i 10) (goto :loop:)))
0)

если с помощью goto выйти из циклов — ещё можно понять, а как заходить? Там же нужно всякие стек-фреймы инициализировать и прочее. 
Нет, у меня каша в голове с goto и lisp-ом :) Попробую погуглить.

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

> не всегда.

Я говорю про define в top level. Scheme от других языков в этом смысле ничем не отличается, семантика выражений top level не совместима с семантикой внутренних выражений.

> если с помощью goto выйти из циклов — ещё можно понять, а как заходить?

Если goto в пределах одного контекста - то всё ок. А те же локальные переменные должны внутри одной функции инициализироваться в одном месте.

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