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

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

Как-то интересно вы программируете…
А на чем вы программируете?
НЛО прилетело и опубликовало эту надпись здесь
А вы программируете?
А как вы начали программировать?..
А где вы программируете?
Кто здесь?
image
в хроме от минусов скроллбар почернел и отвалился :(
В опере вообще нет скроллбара )
Полосы черные иногда возникают при прокрутке? Если да, то выключите аппаратное ускорение в Хроме.
Зачем я здесь?
Программируешь небось?
Уши надо чистить по утрам :-)
Желательно не компотом. ©
Идите все в жопу! :)
«Синтаксический сахар вызывает рак точки с запятой» (с) Не помню, кто сказал, но очень правильно заметил, что ни одна возможность не достаётся бесплатно
Известен афоризм Алана Перлиса: «Синтаксический сахар вызывает рак точек с запятой». Точка с запятой («;»), являясь обязательной частью большинства популярных языков программирования, даже если в новом языке бесполезна, оставляется как необязательный элемент, так как большинство программистов имеют прочную привычку её использования. В оригинале афоризм обыгрывает созвучие английских слов semicolon (точка с запятой) и colon, последнее из которых означает не только двоеточие, но и прямую кишку (colon cancer — рак прямой кишки).

© Wikipedia
Прочитал недавно этот афоризм из SICP. Вообще, книжка хорошо прочищает мозг. Особенно понравился тезис о том, что циклы и переменные — тоже синтаксический сахар, без которого можно обойтись.
Страница 225 (в PDF — 226), сноска 11 и абзац из которого она ведёт. :3
Опять же провокационный заголовок для того чтобы сильнее задуматься над смыслом и делать не по привычке, а с умом. Молодец автор, заставил задуматься.
Ведь и goto когда-то может пригодится. Хотя мне ни разу не пригодился пока что =)
НЛО прилетело и опубликовало эту надпись здесь
В языках без goto обычно он просто ненужен, т.к. есть другие средства. При программировании на Tcl, например, мне ни разу не попадалась такая задача, где бы он был нужен.
НЛО прилетело и опубликовало эту надпись здесь
Perl, тройной вложенный цикл, выход из самого внутреннего цикла. без goto и эксепшенов.

LOOP1: for (my $i = 0 ; $i < 100; $i++) {
  LOOP2: for (my $j = 0; $j < 100; $j++) {
    LOOP3: for (my $n = 0; $n < 100; $n++) {
      last LOOP1 if $some_condition;
    }
  }
}
НЛО прилетело и опубликовало эту надпись здесь
Нет, goto плох тем, что программист может поставить метку куда угодно, тем самым испортив программу. А last может перейти только туда, куда позволяет язык.

То есть last = связанный по рукам и ногам goto, и всего лишь-то. По-моему, честно.
НЛО прилетело и опубликовало эту надпись здесь
Неправильно судить о таком мощном средстве как goto по одному примеру. То, что «в конкретном выше озвученном примере last и goto — это одно и то же по своему поведению/эффекту», почти ничего не значит.
Вообще-то break это тоже goto, просто оно не позволяет яйца дверью защемить.
хех. не надо уж так обобщать. в этом смысле даже условие выхода из цикла — тоже goto, особенно, если посмотреть, какой код генерит из этого компилятор =)
Любой цикл можно запилить связкой if + goto, получается всё остальное — это сахар.
Точно, доиграться можно и не до такого. Об этом и говорю: не надо всё обобщать.
goto плох тем, что далеко не всегда сходу понятно, куда идёт переход. Особенно в условиях совмещённого использования goto и процедурных вызовов.

С break/continue всё предельно ясно: переходы происходят в пределах цикла, на стек не влияют (или влияют минимально и легко предсказуемо: только в разрезе локальных переменных, объявленных внутри цикла). Границы цикла обычно в пределах одного экрана (а если нет, то уже это одно — хороший повод для рефакторинга), т.е. workflow можно окинуть одним взглядом.

С goto всё совсем иначе. особенно в языках, где переходить можно между функциями. Я уже не говорю о setjmp()/longjmp(), где вызовы вообще могут быть где угодно.
НЛО прилетело и опубликовало эту надпись здесь
И это, имхо, одна из самых некрасивых вещей в питоне.
Алсо, в руби это решается так:

catch :megabreak do
  for a in b
    for c in d
      throw :megabreak
    end
  end
end


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

begin
  raise Exception
rescue e
end


Да, это синтаксический сахар, но разве это плохо?
Перловый вариант покрасивше будет. Да и по смыслу лучше — ты говоришь last или next поименнованному циклу, тут же ты выбрасываешься наружу. Синтаксис для настоящих самураев.
sprunge.us/AgaO?cl

Кто там покрасивше? :]

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

а с другой стороны — ексепшоны это всеголишь разновидность yield.
НЛО прилетело и опубликовало эту надпись здесь
мы, конечно, в оффтоп уползли.
но неужто реально можно вернуться из исключения обратно?
Обычно такие штуки решаются грамотным структурированием программы. Хотя, конечно, бывают и исключения.
НЛО прилетело и опубликовало эту надпись здесь
В PHP можно писать break 3;
И как бы ни ругалм такой синтаксис, иногда его использование выглядит логичнее всех остальных способов. В C# мне такого синтаксиса иногда не хватает.
НЛО прилетело и опубликовало эту надпись здесь
В C, да, согласен, задачи есть. В высокоуровневых же языках такого рода задачи обычно уже решены на более низких уровнях. Хотя мне, например, при всём моём ультранизкоуровневом программировании на C тоже ни разу ещё goto не понадобилось, как-то так вот :)
НЛО прилетело и опубликовало эту надпись здесь
Гораздо интереснее когда goto ведет из одной функции не в прежнюю, а в совсем другую. Вот веселуха потом с кодом таким работать :).
Ага, прощай стек, да здавствуют утечки памяти и вообще, вызов деструкторов у временных обьектов это всё для школоты!
Ага, и стандарт языка C и функцией longjmp() писала школота.

Вы сначала попробуйте сделать выход из глубокого слоя вложенных рекурсивно вызываемых функций без этого самого longjmp(), а потом издевайтесь. При этом я не говорю, что с ним гемороя нет, с освобождением памяти тут можно долго мучатся, но иные методы (вроде оборачивания всего слоя функций в макросы или создание своего стека) ещё более ужасны.
Вы можете указать задачу для такой техники выламывания стека вызовов?
Пожалуйста.
1) Обработка ошибок;
2) Прерывание рекурсивного обхода дерева.
1) не через исключения?

2) обход дерева всегда имеет цель. может быть лучше GoalReached = TRUE?
1) Исключения в C? Если бы…
2) Например, у нас может быть синтаксическое дерево какого-то языка и я хочу его выполнить. Но внезапно я встретил команду break, и мне нужно перейти в тот уровень, в котором обрабатывается ближайший цикл. В объектно-ориентированном языке для этого можно использовать исключения, в C либо чем-то ужасным, либо longjmp'ом.
1) на венде есть __try, __except, __finally вроде бы?

2) понятно. то есть стек вызова выделенных данных не содержит?
1) А это всё некроссплатформенно
2) Стек может содержать всё что угодно. При вызове longjmp он восстанавливается к состоянию, которое было на момент setjmp (см. вики). Естественно, проблемы с тем, что выделенную между вызовами память надо освободить, возникают.
Итого, если я ничего не упустил, смысл статьи:
1. «Синтаксический сахар» заставляет мыслить шаблонно.
2. Из первого следует, что он сужает мышление и творческий потенциал разработчиков.
3. Мораль: будьте бдительны думайте, думайте и еще раз думайте.

Наверное, банальные вещи иногда полезно разжевывать, тут спорить особенно не с чем.

PS у лиспа есть синтаксис :)
НЛО прилетело и опубликовало эту надпись здесь
Есть! <irony/>
Ах, какая пустая ирония!
;)
У лиспа есть синтаксис в такой же мере, как синтаксис у xml или json.
А я вот что то не понял из статьи :(.
Ведь
from operator import attrgetter
names = map(attrgetter("name"), fields)

это и есть синтаксический сахар, а автор назвал сахаром цикл?
И если над кодом будет работать другой человек, он гораздо быстрее поймет код с циклом. Ради чего мучить других людей?
НЛО прилетело и опубликовало эту надпись здесь
1) Без циклов можно обойтись (рекурсия)
2) Цикл — часть синтаксиса языка

Следовательно цикл — это синтаксический сахар.

Без вызова же функций/процедур/рутин обойтись сложнее.
да и вообще, достаточно только ветвления и вызова функции, всё остальное — сахар.
(пруф: www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-7.html#%_sec_4.1)

переменные тоже не нужны как класс, поскольку связывание есть в вызове функции. (более того — они вредны)
Понятие сахара относительно. Можно все циклы делать через map, можно все отображения (map) делать через for. Выбор зависит от предпочтений для каждого конкретного случая.

Я хочу отобразить список полей в список их имён поэтому map, кажется более подходящим.

Насчёт того, в чем другой человек быстрее разберётся — это зависит уже от его опыта и его предпочтений, но в вакууме, чем короче код или точнее чем меньше в нём синтаксических элементов, тем быстрее он читается.
По сравнению с brainfuck-ом, любой язык черезвычайно переслащен :)
Творческий потенциал, может быть, статья не об этом. Сужение мышления уже ближе.
Мораль в том, что вы можете не увидеть возможность понижения уровня абстракции кода из-за кажущейся неделимости синтаксических конструкций. А понизить уровень абстракции бывает необходимо, чтобы устранить дублирование или просто добавить гибкости.
my $names = map { ($alias ? $alias.'.':'').$_ } @fields;

Perl может быть лаконичным
Если решать только исходную, не обобщённую задачу и не забывать про name:

my @names = map {$_->name} @fields;

, что, учитывая, то, что блок в map — закамуфлированная анонимная функция, аналогично второму варианту на js.

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

Конечно, для языков где декларативный код — это синтаксический сахар над императивным, вроде C#, они совпадают.
НЛО прилетело и опубликовало эту надпись здесь
Совершенно верно. Нужно учитывать контекст.
НЛО прилетело и опубликовало эту надпись здесь
Знаете, сахар сахаром, а по мне — это не сахар, а кофеин. Синтаксический кофеин.
Он позволяет вам написать код быстрее, тем самым получив первый результат уже сегодня, а не завтра после борьбы с тривиальными вещами.
Нужна скорость? После первой итерации убирается сахар и все.

Что касается новичков, то, привыкание к сахару-кофеину, вне сомнения, напрягает. Видимо потому, что мало кто их обучает с основ, обычно на словах пояснили основы и сразу в бой и потом гонят вперед.
Далой массивы! Пора слезть с нефт^W сахаро-кофеиновой иглы!
По-моему, автор описал недостатки преждевременной оптимизации, а не вредность синтаксического сахара.
Ну понятно что большие выражение нужно выносить в функции, только это и для простого for тоже верно.
Ну и мой вариант:
ext_name = lambda field: alias and "%s.%s" % (alias, field.name) or field.name
names = [ext_name(field) for field in fields]

#or:
names = map(ext_name, fields)

#as you wish
«Это уже не выглядит так здорово, а что если нам потребуется приписать алияс для поля?»

А что если завтра метеорит упадёт на землю?

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

Вот понадобится приписать алиас — тогда и припишете (возможно переписав этот участок кода). А пока этого не нужно — не придумывайте себе дополнительную головную боль.
Тут все на ASM-е пишут что-ли?
Нет?
А почему, ведь всё остальное — своего рода синтаксический сахар…
ASM тоже синтаксический сахар. Настоящие программисты пишут при помощи бабочек.
Глупости.

> Короче, симпатичнее и куда ближе к тому, как я описывал алгоритм: пробежаться по полям — fields.map, получить имя — функция, которая получает имя. Н-да, функция, как бы от неё избавиться? Легко, не будем сдерживать себя, перепишем всё на питоне:

Во-первых, с какой стати перепрыгиваем с языка на язык?

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

Вы так сказали, будто это плохо. А если кто-то не знает, что происходит на самом деле, значит, он плохой программист.

> Одна строчка! Казалось бы, мы достигли идеала, но на самом деле этот вариант в некотором смысле даже хуже предыдущего: его тяжело расширять и практически невозможно повторно использовать.

Что за преждевременная оптимизация? Дайте линейкой себе по рукам, когда хочется сделать что-то на будущее. Потому что реально «вещи на будущее» не нужны почти никогда, а когда нужны — все равно не в таком виде, и вы сейчас не способны предсказать, в каком виде будут нужны.

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

Заблуждение. Сразу хороший код никогда не бывает. А без синтаксического сахара вы бы думали так: «Если бы у меня был синтаксический сахар, я бы сразу написал хороший и понятный код».
То, о чём вы предостерегаете и есть синтаксический диабет.
В лиспе есть макросы.
По сути, тотже синтаксический сахар, только более высокого уровня, а главное — управляемый.
Но и более взрывоопасный для мозга.
Может, и взрывоопасный, но зато уникальный по своей мощи и гибкости. После Common Lisp и Scheme так не хватает макросов, когда приходится возвращаться с небес (десктопа) на землю, к микроконтроллерам с их суровым и непреклонным C. Одна надежда на Typed Racket — там и статическая типизация, и так нами любимый синтаксический сахар (:
С лиспа к микроконтроллерам — это, пожалуй даже — падать. :)

Хотя вот NASAвские роботы программировались на чём-то лиспо-подобном вполне на уровне контроллеров.
(define-module differential
  (drive motor-direction-bits
         (case vehicle-velocity-command
               ((-127 -1) #b00010110)
               ((1 127) #b00011001)
               (else 0)))
  (drive steering-motor-position-command
         (- 10 vehicle-steering-command))
  (let ((velocity
         (* vehicle-velocity-command 16))
        (offset
         (* vehicle-velocity-command
            vehicle-steering-command 2)))
    (drive left-motor-velocity-command
           (abs (+ velocity offset)))
    (drive right-motor-velocity-command
           (abs (- velocity offset)))))

[ALFA: A Language for Programming Reactive Robotic Control Systems, Erann Gat @robotics.jpl.nasa.gov, 1991]
Если бы кто-то рядом писал что-то на лиспоподобном, я бы его убил за ярко выраженный эгоизм.
Я так думаю, в jpl.nasa.gov не совсем дураки работают, и не просто так правительственные деньги пилят.
И если они предлагают использовать лисп для программирования высокоотказных супернадёжных спутников и роботов — значит, на то есть веские причины.
Например: элементарный синтаксис и структура проги, паралеллизм, безстэковая рекурсия.

Конкретно в этом проекте (архитектура 'atlantis', хз где была в реале, если вообще) использовались хитрожопые ексепшоны с возвратом к прерванной задаче после очередной попытки исправления ситуации. На лиспе описывается также естественно как и всё остальное.

Ну и всякий там искуственный интеллект для обруливания препятствий в условиях хреновой видимости и отказа датчиков, и прокладки маршрутов между образцами с учётом запаса горючего и форсмажа в виде выскочившего инопланетянина с бластером и непреодолимым желанием отобрать ценный образец.
И что-то мне подсказывает, что написание подобного AI на асемблере легко приведёт к самоубийству за ярко выраженный идиотизм. :)
А я и не говорю что это плохо, просто дико им завидую. :)
Используй силу, Люк. ©
ну не всех устраивает микроконтроллеры на сях программить — github.com/voidlizard/hopc
Интересно, что сам оператор for не подвергся анафеме. Ведь есть while. И ведь действительно не все умеют использовать while, а используют for и break.
На самом деле — подвергся. Практически во всех более или менее динамически типизированных языках рекомендуется использовать for each для перебора элементов массива (множества), а задержку вроде
for i=0 to 10 {sleep(100)}
уже давно использовать бесполезно из за оптимизирующих компиляторов.

Тссс! Даже в Delphi теперь…
позор мне!!! реально забыл про while и использую for и break.
побежал исправлять…
Как говорил мой технический директор, когда только начал программировать: — Не в строчках счастье.

Важно, чтобы было понятно.
Даже не чтобы понятно.
Важно, чтобы все работало так, как хочется, и можно было добавить все, чего хочется и еще нету. Со всеми вытекающими.
Я не совсем уверен, что LINQ в .net можно отнести к сахару, но в пору, когда я писал на шарпе, я очень часто использовал понятные и лаконичные конструкции linq вместо стандартных циклов и условий. И на мой взгляд код становился более лаконичным. Что касается расширяемости, то в случаях, когда возникала такая необходимость, модификация linq запросов труда не вызывала.

Мне кажется, что во всем нужно знать меру. Во многих случаях сахар языка помогает написать быстрее и лаконичней. Но если не знать меру, то можно увлечься и создать монстра, которого все будут бояться )
Вам нравится обращаться к свойствам объекта через точку? Мне — очень, это кратко (кроме самого объекта и требуемого свойства присутствует только маленькая точка) и экспрессивно (даже человек далёкий от объектов легко поймёт в чём тут соль).


Экспрессивно? Может, выразительно?
В одном из смыслов экспрессивность = выразительность, возможно, я перечитал англоязычной литературы
Какая задача решалась? Сделать код понятнее? List comprehension понятнее и «общепринятее». Сделать код быстрее? List comprehension быстрее раза в два на моем компьютере. Упростить поддержку кода? Да одно и то же.

Да и гибкости у вашего кода мало. А что, если атрибут будет называться не 'name'? А что, если у разных полей атрибут будет называться по-разному? А что, если fields вдруг будет не iterable? Тут до гибкости еще работать и работать. :-)
Уважаемый, оберните, пожалуйста, код в <source/>, а не <blockquote><pre>
<source lang="{lang}">code</source>

Типа
var names = [];
for (var i = 0, l = fields.length; i < l; i++) {
     names.push(fields[i].name);
}

Или
if alias:
     get_name = lambda field: "%s.%s" % (alias, field.name) 
else:
     get_name = lambda field: field.name
 names = map(get_name, fields)
Пытаться писать расширяемый код — дурная затея. Всегда нужно писать наиболее красивый вариант под текущие требования. И быть готовым что-то переписать при появлении новых требований. А то, что синтаксический сахар загоняет в колею — это проблема программиста. Если не уметь выскакивать из таких «ограничителей» то вообще хороший код писать не получится.
На самом деле, вполне логичное рассуждение. Скажем и себя постоянно ловил, что при итерировании в Ява в foreach стиле из-за того, что нет доступа к итератору, мне проще удаление делать через левую коллекцию, а не напрямик через итератор.
Но вот аргумент, про усложнение расширения это конечно не совсем так, потому что для этих целей и придуман рефакторинг. Если некая сущность настолько усложнилась, что она плохо читается в строчку, ее всегда можно выделить отдельно. По сути все программирования постоянно меняет такие штуки, боятся этого просто бессмысленно. По мере того, как качества кода падает ниже определенной планки, это хороший повод либо отрефакторить кусок, либо переписать даже, возможно.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории