В ревизиях 9018 и 9019 появились новые макросы foreach / else, while / else.
Эти конструкции создают неоднозначность с if / else, что приводит к ломающим изменениям и заставляет писать лишние скобки.
Уверен, что конструкция:
if (x)
foreach (y in ys)
z(y);
else
someElse();
применяется чаше чем:
foreach (y in ys)
z(y);
else
someElse();
Может быть лучше сделать синтаксис другим? Например, можно сделать так:
foreach (y in ys)
z(y);
loop else
someElse();
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>В ревизиях 9018 и 9019 появились новые макросы foreach / else, while / else. VD>Эти конструкции создают неоднозначность с if / else, что приводит к ломающим изменениям и заставляет писать лишние скобки.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, _nn_, Вы писали:
__>>Если otherwise нигде не занято, можно взять его.
VD>Оно используется в assertions.n, но по идее конфликтов быть не должно. Или они будут настолько редки, что этим можно пренебречь.
А если не секрет как можно создать там конфликт ?
У меня никак for/while/foreach не удается вписать в блок require.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Mystic, Вы писали:
M>>
M>>foreach (y in ys)
M>> z(y);
M>>fiasco
M>> someElse();
M>>
VD>Это как из кроссворда: Слово из шести букв, вторая буква "и", означающее "крушение всех надежд". VD>
Ну да. Вообще-то я искал английский аналог для нашего родного "облом". Чтобы было с юмором (а это всегда лучше запоминается и лучше акцентируется). Плюс слово достаточно редкое, вряд ли я припомню хотя бы один идентификатор с таким именем. otherwise более официально.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, _nn_, Вы писали:
__>>А если не секрет как можно создать там конфликт ?
VD>Х.з. когда создастся, то сам увидишь .
__>>У меня никак for/while/foreach не удается вписать в блок require.
VD>А что этому мешает? Он не принимает void-выражения?
Да.
class A
{
public F(i : int) : int
requires ()
{
i
}
}
Error: expected bool, got void in matched value: the types void and bool are not compatible [simple unify]
А блок тоже нельзя вставить:
class A
{
public F(i : int) : int
requires { i > 1 }
{
i
}
}
Error: parse error near '{...}' group: expecting type declaration
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, catbert, Вы писали:
C>>А что они делают и для чего понадобились?
VD>Мне кажется это ясно из подписи к комиту и прилагаемых тестов: VD>http://code.google.com/p/nemerle/source/detail?r=9018
то есть они делают совсем не то, что в питоне делают for-else (else-тело выполняется, если цикл не завершился break-ом) и while-else (выполняется, если цикл завершился по false == условие цикла), а выполняют else-тело, если основное тело не было вызвано ни разу. Все три варианта поведения мягко говоря не интуитивны. Как минимум, в документации должно быть отражено отличие от python.
Здравствуйте, Иванков Дмитрий, Вы писали:
ИД>Здравствуйте, VladD2, Вы писали:
VD>>Здравствуйте, catbert, Вы писали:
C>>>А что они делают и для чего понадобились?
VD>>Мне кажется это ясно из подписи к комиту и прилагаемых тестов: VD>>http://code.google.com/p/nemerle/source/detail?r=9018
ИД> то есть они делают совсем не то, что в питоне делают for-else (else-тело выполняется, если цикл не завершился break-ом) и while-else (выполняется, если цикл завершился по false == условие цикла), а выполняют else-тело, если основное тело не было вызвано ни разу. Все три варианта поведения мягко говоря не интуитивны. Как минимум, в документации должно быть отражено отличие от python.
Насчет break-ов действительно не подумал.
Доделать не сложно
Здравствуйте, _nn_, Вы писали:
__>Он прекрасно работает в коде и даже проходит тесты. __>Одна загвоздка в нем. Его нельзя использовать в макросах. __>Как всегда вопросы: кто виноват и что делать ?
Насколько мне известно в квази-цитатах используется все тот же парсер, так что по идее если код компилируется вне цитаты, то в цитате тоже должен. Так что надо смотреть под отладчиком что происходит.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Иванков Дмитрий, Вы писали:
ИД> то есть они делают совсем не то, что в питоне делают for-else (else-тело выполняется, если цикл не завершился break-ом)
Э... а разве не в случае когда в коллекции нет элементов? Кстати, я бы еще хотел чтобы чтобы на null тоже else срабатывал.
ИД>и while-else (выполняется, если цикл завершился по false == условие цикла), а выполняют else-тело, если основное тело не было вызвано ни разу.
Тут я вообще ничего не понял. Мне казалось, то любой вид цикла должен вызвать else если основной цикл не сработал ни разу. Зачем еще какие-то "завершился по false == условие цикла"?
ИД>Все три варианта поведения мягко говоря не интуитивны. Как минимум, в документации должно быть отражено отличие от python.
В общем, я стал понимать еще меньше чем в начале. Можно объяснить все более подробно и доступно (для тех кто с Питоном не на "ты")?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, _nn_, Вы писали:
__>>Как всегда вопросы: кто виноват и что делать ?
VD>А почему ты не остановился на отдельном макросе?
Мне надо otherwiseBody поместить в блок _N_Break, как раз одним макросом все красиво решалось..
Здравствуйте, _nn_, Вы писали:
__>Мне надо otherwiseBody поместить в блок _N_Break, как раз одним макросом все красиво решалось..
Ясно.
А зачем в _N_Break?
Но, проблема в том, что foreach уже и так очень перегружен. Добавление к нему еще кода усложнит его многократно.
__>Получилось локализовать проблему, вроде: __>
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, _nn_, Вы писали:
__>>Мне надо otherwiseBody поместить в блок _N_Break, как раз одним макросом все красиво решалось..
VD>Ясно.
VD>А зачем в _N_Break?
Для соответствия со спецификацией Python: http://docs.python.org/reference/compound_stmts.html
Вкратце, если вызвался break,то не вызывать otherwiseBlock.
__>>Почему в FFF нужна ";" между while1 и скобками ?
VD>Без пары часов под отладчиком я тебе на этот вопрос не отвечу . VD>Но если приглядеться к реализации if/else, то можно заметить некий трюк: VD>
Здравствуйте, _nn_, Вы писали:
VD>>А зачем в _N_Break? __>Для соответствия со спецификацией Python: http://docs.python.org/reference/compound_stmts.html __>Вкратце, если вызвался break,то не вызывать otherwiseBlock.
Погоди, но разве он вообще должен вызваться если мы вошли в тело цикла? Мне показалось, что твоя реализация при первом же входе поднимает флаг предотвращающий вызов otherwise при входе в тело цикла.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали: VD>В общем, я стал понимать еще меньше чем в начале. Можно объяснить все более подробно и доступно (для тех кто с Питоном не на "ты")?
Смысл примерно такой, что перед каждым входом в тело цикла проверяется условие, и в случае false цикл просто прерывается, вот перед этим моментом вставляется else-выражение.
То есть для for/while вызов происходит при выходе по условию цикла, но не по break.
С Питоном я тоже не на "ты", но вроде бы обычно это используют для циклов, осуществляющих какой-нибудь поиск.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, _nn_, Вы писали:
VD>>>А зачем в _N_Break? __>>Для соответствия со спецификацией Python: http://docs.python.org/reference/compound_stmts.html __>>Вкратце, если вызвался break,то не вызывать otherwiseBlock.
VD>Погоди, но разве он вообще должен вызваться если мы вошли в тело цикла? Мне показалось, что твоя реализация при первом же входе поднимает флаг предотвращающий вызов otherwise при входе в тело цикла.
Здравствуйте, Иванков Дмитрий, Вы писали:
ИД> то есть они делают совсем не то, что в питоне делают for-else (else-тело выполняется, если цикл не завершился break-ом) и while-else (выполняется, если цикл завершился по false == условие цикла), а выполняют else-тело, если основное тело не было вызвано ни разу. Все три варианта поведения мягко говоря не интуитивны. Как минимум, в документации должно быть отражено отличие от python.
When the items are exhausted (which is immediately when the sequence is empty), the suite in the else clause, if present, is executed, and the loop terminates.
Я правильно понимаю, что в Питоне else вызывается даже если коллекция не пуста?
Если это так, то кто-нибудь может объяснить великий смысл этого этого подхода? Т.е. зачем так делать?
Мне кажется, что в этом просто нет смысла. Вот else в случае если коллекция пуста или null имеет смысл, так как это позволяет нам обработать нештатную ситуацию (хотя ее и так не сложно обработать проверив длину коллекции). Но какой смысл в вызове else для не пустой коллекции? Да звучит "else" при этом не интуитивно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Иванков Дмитрий, Вы писали:
ИД>http://docs.python.org/reference/compound_stmts.html#the-while-statement ИД>http://docs.python.org/reference/compound_stmts.html#the-for-statement
ИД>Смысл примерно такой, что перед каждым входом в тело цикла проверяется условие, и в случае false цикл просто прерывается, вот перед этим моментом вставляется else-выражение. ИД>То есть для for/while вызов происходит при выходе по условию цикла, но не по break. ИД>С Питоном я тоже не на "ты", но вроде бы обычно это используют для циклов, осуществляющих какой-нибудь поиск.
На мой взгляд какое-то совершенно бессмысленное поведение (если я его правильно понял).
Вот выполнить действия если в тело цикла не попали вовсе смысл имеет.
Или тогда уж нужно называть эту конструкцию не else/otherwise, а after, что ли.
В общем, хотелось бы увидеть некий случай использования питоновского поведения. А то пока я что-то не пойму какую цель решает их реализация.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
ИД>>Все три варианта поведения мягко говоря не интуитивны. Как минимум, в документации должно быть отражено отличие от python. VD>В общем, я стал понимать еще меньше чем в начале. Можно объяснить все более подробно и доступно (для тех кто с Питоном не на "ты")?
Питоновский for — это аналог немерловского и шарпового foreach, итерирует некую последовательность, помещая очередной элемент в заданную переменную. Else там выполняется тогда, когда больше нечего итерировать, т.е. после того, как достигнут конец последовательности. Если последовательность пуста, то else выполняется сразу. Соответственно, если из цикла вышли по break, то else не выполняется (т.к. не был достигнут конец последовательности), если из итерации вышли по continue, то else выполняется, если на текущий момент в последовательности не осталось элементов.
Питоновский while — по логике аналогичен одноименному циклу в немерле и шарпе. Пока выполняется какое-либо условие, выполняется тело цикла, как только условие перестало выполнятся, то однократно выполняется else и цикл завершается. Если из цикла вышли по break (или из итерации по continue), то else не выполняется.
Иными словами, else — это набор инструкций, которые должны быть выполнены в том случае, если цикл уже завершен, но только в том случае, если в ходе работы цикла были осуществлены все запланированные по его логике итерации.
Здравствуйте, Иванков Дмитрий, Вы писали:
ИД>http://docs.python.org/reference/compound_stmts.html#the-while-statement ИД>http://docs.python.org/reference/compound_stmts.html#the-for-statement
ИД>Смысл примерно такой, что перед каждым входом в тело цикла проверяется условие, и в случае false цикл просто прерывается, вот перед этим моментом вставляется else-выражение. ИД>То есть для for/while вызов происходит при выходе по условию цикла, но не по break. ИД>С Питоном я тоже не на "ты", но вроде бы обычно это используют для циклов, осуществляющих какой-нибудь поиск.
Иными словами else срабатывает всегда за исключением случая выхода из цикла по break?
Единственное что приходит на ум по поводу "зачем это надо" — это эмуляция метода Find или даже FindWithDefault.
Есть ли какие-то еще варианты использования для питоновского else-а?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, kochetkov.vladimir, Вы писали:
KV>Иными словами, else — это набор инструкций, которые должны быть выполнены в том случае, если цикл уже завершен, но только в том случае, если в ходе работы цикла были осуществлены все запланированные по его логике итерации.
Это я уже понял. Но какой в этом практический смысл?
Каковы варианты использования этого дела? Другими словами что это дает на практике?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Я правильно понимаю, что в Питоне else вызывается даже если коллекция не пуста? VD>Если это так, то кто-нибудь может объяснить великий смысл этого этого подхода? Т.е. зачем так делать? VD>Мне кажется, что в этом просто нет смысла. Вот else в случае если коллекция пуста или null имеет смысл, так как это позволяет нам обработать нештатную ситуацию (хотя ее и так не сложно обработать проверив длину коллекции). Но какой смысл в вызове else для не пустой коллекции? Да звучит "else" при этом не интуитивно.
else в питоне предназначен как раз для обратного: для обработки ситуации, когда цикл отработал штатно и не прервался каким-либо образом.
Здравствуйте, kochetkov.vladimir, Вы писали:
KV>else в питоне предназначен как раз для обратного: для обработки ситуации, когда цикл отработал штатно и не прервался каким-либо образом.
Понял, понял я уже. Я не понял зачем это все нужно? Для чего это все используется?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Есть ли какие-то еще варианты использования для питоновского else-а?
В общем, идей, почему Гвидо впихнул туда этот синтаксис нет. Скорее всего, чтобы сделать более красивой работу с циклами, в которых возводятся какие-либо булевы флаги.
Как еще один из возможных вариантов применения — возврат циклом каких-либо значений, т.е. сделать цикл псевдо-выражением.
Здравствуйте, kochetkov.vladimir, Вы писали: KV>Здравствуйте, VladD2, Вы писали:
VD>>Есть ли какие-то еще варианты использования для питоновского else-а? KV>В общем, идей, почему Гвидо впихнул туда этот синтаксис нет.
Все-таки вот это.:
KV>Скорее всего, чтобы сделать более красивой работу с циклами, в которых возводятся какие-либо булевы флаги.
Он ввел это в синтаксис именно для того, чтобы в циклах с break была возможность разветвлить control-flow в зависимости от того, каким образом был осуществлен выход из цикла.
Здравствуйте, kochetkov.vladimir, Вы писали:
KV>Как еще один из возможных вариантов применения — возврат циклом каких-либо значений, т.е. сделать цикл псевдо-выражением.
Все равно не ясно что возвращать при этом.
ЗЫ
Пока что у меня усиливается ощущение, что данная фича добавляется чтобы говорить другим "у нас это тоже есть". А это мне категорически не нравится. В язык нужно пихать только то, что используется часто и дает видимый эффект.
ЗЫЫ
Подозреваю что else в Питоне был создан для поиска значений в коллекциях путем их полного перебора. Но циклы для этого не очень подходящий механизм. Намного удобнее для этого использовать методы Find, FindIndex, Where, Filter.
Так может быть вместо этого самого else/otherwise лучше создать некий макрос find, который позволит сделать синтаксис поиска более компактным, а сам поиск более эффективным? Синтаксис может быть примерно таким:
"find" "(" id "when" condition ("with" index)? "in" collection ")" body ("notfound" expr)?
где
id : PExpr.Ref
condition : PExpr
index : PExpr
collection : PExpr
body : PExpr
expr : PExpr
Примеры использования:
def res = find (x when x > 10 && x % 2 == 0 in [1 .. 100]) x * 42 notfound 0;
// или императивный (возвращающий void) вариант:
mutable res;
find (x when x > 10 && x % 2 == 0 in [1 .. 100])
res = x; // тут...
notfound
res = 0; // и тут, естественно, могут быть любые выражения, в том числе блок кода.
Вариант с индексом и без notfound:
def ary = arry[1 .. 100];
find (x when x > 10 with i && x % 2 == 0 in )
ary[i] = x * 42; // тут...
Это будет намного удобнее нежели не очевидный otherwise в циклах, которые в Немерле очень редко используются (людьми хорошо знакомыми с Немерле) для поиска значений.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Пока что у меня усиливается ощущение, что данная фича добавляется чтобы говорить другим "у нас это тоже есть". А это мне категорически не нравится. В язык нужно пихать только то, что используется часто и дает видимый эффект.
+1
VD>Так может быть вместо этого самого else/otherwise лучше создать некий макрос find, который позволит сделать синтаксис поиска более компактным, а сам поиск более эффективным? Синтаксис может быть примерно таким: VD>
VD>"find" "(" id "when" condition ("with" index)? "in" collection ")" body ("notfound" expr)?
VD>
Вместо id "when" condition нужен полноценный паттерн.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Да, кстати в питоне есть аналогичная конструкция (точнее, набор конструкцией, решающих аналогичную задачу и выглядящих очень похоже) и используется гораздо чаще, чем else в циклах.
VD>нежели не очевидный otherwise в циклах, которые в Немерле очень редко используются (людьми хорошо знакомыми с Немерле) для поиска значений.
Тут еще такая мысль появилась... Сейчас к телам циклов предъявляется требование, чтобы они имели тип void, насколько я понимаю. Соответственно, у цикла также нет возвращаемого значения. А вот если бы цикл был выражением, возвращающим список, элементами которого бы являлись значения, полученные на каждой из итераций, то тогда попадание в otherwise можно было бы завязать на условие того, что возвращенный список является пустым.
Здравствуйте, kochetkov.vladimir, Вы писали:
KV>Тут еще такая мысль появилась... Сейчас к телам циклов предъявляется требование, чтобы они имели тип void, насколько я понимаю. Соответственно, у цикла также нет возвращаемого значения. А вот если бы цикл был выражением, возвращающим список, элементами которого бы являлись значения, полученные на каждой из итераций, то тогда попадание в otherwise можно было бы завязать на условие того, что возвращенный список является пустым.
Не, фигня, на вложенных и развесистых циклах где эта фича не потребуется, GC охренеет
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: По поводу foreach / else
От:
Аноним
Дата:
26.07.10 17:04
Оценка:
Здравствуйте, VladD2, Вы писали:
VD>Может быть лучше сделать синтаксис другим? Например, можно сделать так:
foreach (y in ys) {
} on empty list {
// вызывается если foreach ни разу не сработал
} on one item exist in list {
// вызывается если foreach сработал один раз
} on break {
// вызывается если в foreach был вызван break
} on continue {
// вызывается если в foreach был вызван continue
}
или так
try_for_each (y in ys) {
} catch_empty list {
// вызывается если foreach ни разу не сработал
} catch_one_item_exist_in_list {
// вызывается если foreach сработал один раз
} catch_break {
// вызывается если в foreach был вызван break
} catch_continue {
// вызывается если в foreach был вызван continue
}
Здравствуйте, kochetkov.vladimir, Вы писали:
VD>>Это будет намного удобнее
KV>Да, кстати в питоне есть аналогичная конструкция (точнее, набор конструкцией, решающих аналогичную задачу и выглядящих очень похоже) и используется гораздо чаще, чем else в циклах.
так ее увидит больше народа.
VD>>нежели не очевидный otherwise в циклах, которые в Немерле очень редко используются (людьми хорошо знакомыми с Немерле) для поиска значений.
KV>Тут еще такая мысль появилась... Сейчас к телам циклов предъявляется требование, чтобы они имели тип void, насколько я понимаю. Соответственно, у цикла также нет возвращаемого значения. А вот если бы цикл был выражением, возвращающим список, элементами которого бы являлись значения, полученные на каждой из итераций, то тогда попадание в otherwise можно было бы завязать на условие того, что возвращенный список является пустым.
В Питоне в else попадают всегда если не был вызван break. Так что этой твоей фразы я вообще не понял.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, kochetkov.vladimir, Вы писали:
KV>>Тут еще такая мысль появилась... Сейчас к телам циклов предъявляется требование, чтобы они имели тип void, насколько я понимаю. Соответственно, у цикла также нет возвращаемого значения. А вот если бы цикл был выражением, возвращающим список, элементами которого бы являлись значения, полученные на каждой из итераций, то тогда попадание в otherwise можно было бы завязать на условие того, что возвращенный список является пустым.
KV>Не, фигня, на вложенных и развесистых циклах где эта фича не потребуется, GC охренеет
ЖЦ то тут причем? Ему вообще все это до лампочки. Объекты умершие до запуска GC попросту не считаются родившимися.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Аноним, Вы писали:
VD>>Может быть лучше сделать синтаксис другим? Например, можно сделать так: А>
А>foreach (y in ys) {
А>} on empty list {
А>// вызывается если foreach ни разу не сработал
А>} on one item exist in list {
А>// вызывается если foreach сработал один раз
А>} on break {
А>// вызывается если в foreach был вызван break
А>} on continue {
А>// вызывается если в foreach был вызван continue
А>}
А>
Что касается on empty list, то первая реализация else это и делала. Но оказалось, что это не тоже поведение что и в Питоне и поведение было решено сделать как в Питоне. А я вот пока что не пойму зачем оно такое нужно.
Что касается остального, то это какой то несусветный перебор. Да и "слишком много букв".
Кроме того не ясно зачем может понадобиться "on one item exist in list"? Вот выполнение для первого элемента, если это удастся реализовать более компактно, могло бы быть весьма полезным. Частенько бывает, что обработка для первого элемента отличается от остальных. Но это все уже совсем не о foreach / else.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: По поводу foreach / else
От:
Аноним
Дата:
26.07.10 17:41
Оценка:
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Аноним, Вы писали:
VD>Что касается on empty list, то первая реализация else это и делала. Но оказалось, что это не тоже поведение что и в Питоне и поведение было решено сделать как в Питоне. А я вот пока что не пойму зачем оно такое нужно.
Я так понял были проблемы с однозначностью в блоке if/else.
Соответственно два предложения по именованию, первое в стиле реакции на события (on click), второй в стиле try/catch.
VD>Что касается остального, то это какой то несусветный перебор. Да и "слишком много букв".
Я полагаю, что отдельные блоки можно и нужно делать опциональными.
К тому же можно и сократить on_empty, on_one_item, on_catch, on_continue.
VD>Кроме того не ясно зачем может понадобиться "on one item exist in list"? Вот выполнение для первого элемента, если это удастся реализовать более компактно, могло бы быть весьма полезным. Частенько бывает, что обработка для первого элемента отличается от остальных. Но это все уже совсем не о foreach / else.
Здравствуйте, Аноним, Вы писали:
VD>>Что касается остального, то это какой то несусветный перебор. Да и "слишком много букв". А>Я полагаю, что отдельные блоки можно и нужно делать опциональными.
Проблема с неоднозначностью снялась предложением nikov-а использовать otherwise.
А>К тому же можно и сократить on_empty, on_one_item, on_catch, on_continue.
Для ключевых слов не принято использовать подчеркивания, а я за традиции.
VD>>Кроме того не ясно зачем может понадобиться "on one item exist in list"? Вот выполнение для первого элемента, если это удастся реализовать более компактно, могло бы быть весьма полезным. Частенько бывает, что обработка для первого элемента отличается от остальных. Но это все уже совсем не о foreach / else.
А>on_one_item это отрицание on_empty.
Это кроме того, частный случай. Чтобы создавать операторы для частных случаев нужно иметь весомые основания.
А>Зачем может понадобиться
А>
То честь, предлагается не смотря на вызов on_one_item все равно вызвать тело цикла после вызвоа on_one_item?
Я понял это так, что on_one_item вызывается вместо тела цикла для первого элемента коллекции.
Но тут встает вопрос насколько это нужно? Ведь тоже самое достигается с помощью:
if (lst.IsEmpty)
Response.Write("Нет товаров")
else
Response.Write("Всего товаров: " + list.Count);
foreach(var item in lst)
Response.Write("Товар: " + item.Name + "\n");
и я намеренно не стал делать поддержки подобных развернутых конструкций. Это нарушает принцип KISS.
Для данного примера код использующий ХМЛ-литералы будет выглядеть так:
<p $when (lst.IsEmpty)>Нет товаров</p>
<p $unless (lst.IsEmpty)>Всего товаров $(list.Length)</p>
<p $foreach (item in lst)>Всего товаров $(item.Name)</p>
с той разницей, что при это генерируется не плоский текст, а XML.
А>Соответственно если товары в списке есть, то выведет список товаров и их количество, если же нет товаров то так и напишет.
Ясно, но это не дает видимого преимущества. При этом программисты (даже те кто не использует эту фичу) будут вынуждены учить все эти прибамбасы в макросах. Так что я бы сказал — овчинка выделки не стоит.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: По поводу foreach / else
От:
Аноним
Дата:
26.07.10 18:00
Оценка:
Здравствуйте, VladD2, Вы писали:
VD>Частенько бывает, что обработка для первого элемента отличается от остальных. Но это все уже совсем не о foreach / else.
то есть если есть блок on_first то для первого элемента коллекции выполняется он
В данном случае для первого товара название будет выведено жирным шрифтом.
Конечно, для достаточно длинного блока foreach будет не так наглядно.
Можно также сделать on_before_first и on_after_first для действий выполняемых перед и после первого элемента.
А>то есть если есть блок on_first то для первого элемента коллекции выполняется он А>В данном случае для первого товара название будет выведено жирным шрифтом. А>Конечно, для достаточно длинного блока foreach будет не так наглядно.
Вроде бы в прошлом примере on_first вызывался не вместо первого элемента, а перед ним.
В любом случае тоже самое можно реализовать сегодняшними средсвами практически не теряя в выразительности и скорости:
foreach(item with index in lst)
if (index == 1)
Response.Write("Товар: <b>" + item.Name + "</b>")
else
Response.Write("Товар: " + item.Name);
А>Можно также сделать on_before_first и on_after_first для действий выполняемых перед и после первого элемента.
А>
Ага, можно. Но опять же вариант с индексом проще и универсальнее ведь он позволит и последний элемент отработать, и четный/не четный и т.п.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: По поводу foreach / else
От:
Аноним
Дата:
26.07.10 18:14
Оценка:
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Аноним, Вы писали:
VD>Проблема с неоднозначностью снялась предложением nikov-а использовать otherwise.
otherwise кстати тоже очень хорошее слово, хочется часто использовать, почти как else.
VD>Для ключевых слов не принято использовать подчеркивания, а я за традиции.
может пробел тогда вместо подчеркивания
VD>Это кроме того, частный случай. Чтобы создавать операторы для частных случаев нужно иметь весомые основания.
Ну почему частный, это отрицание else/otherwise, то есть достаточно общий.
one не означает, что только один элемент в коллекции, а хотя бы один.
VD>То честь, предлагается не смотря на вызов on_one_item все равно вызвать тело цикла после вызвоа on_one_item?
Нет сначало тело цикла, а потом блок on_one_item. Вызовы последовательны, как записано, сначало тело цикла, если удовлетворяет условиям, затем уже блоки on_empty, on_one_item и т.д.
VD>Но тут встает вопрос насколько это нужно? Ведь тоже самое достигается с помощью:
Так такой же вопрос можно задать и для else/otherwise.
VD>
А нужно это может быть затем, что операция определения пустой список или нет может быть сравнительно дорогой.
То есть вызов метода, по сравнению с заведением локальной булевской переменной на стеке
Типа заворачивания в макрос подобного кода.
def empty = true
foreach(item in list) {
empty = false;
}
if (empty) Response.Write("Нет товаров")
if (!empty) Response.Write("Всего товаров: " + list.Count)
Конечно, с некоей оптимизацией, чтобы присваивание было не в каждой итерации, а только в первой.
VD>Ясно, но это не дает видимого преимущества. При этом программисты (даже те кто не использует эту фичу) будут вынуждены учить все эти прибамбасы в макросах. Так что я бы сказал — овчинка выделки не стоит.
Ну это зависит от того, насколько просто добавить.
К тому же как я предложил, можно сделать новый макрос, try_foreach, по аналогии с TryCast, TryParse
То есть тем кто использует foreach все эти фишки учить не надо
Re[5]: По поводу foreach / else
От:
Аноним
Дата:
26.07.10 18:26
Оценка:
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Аноним, Вы писали:
А>>
А>>то есть если есть блок on_first то для первого элемента коллекции выполняется он А>>В данном случае для первого товара название будет выведено жирным шрифтом. А>>Конечно, для достаточно длинного блока foreach будет не так наглядно.
VD>Вроде бы в прошлом примере on_first вызывался не вместо первого элемента, а перед ним.
Там был on_one, а не on_first. Можно вместо on_one использовать on_non_empty или on_full_list.
VD>В любом случае тоже самое можно реализовать сегодняшними средсвами практически не теряя в выразительности и скорости: VD>
VD>foreach(item with index in lst)
VD> if (index == 1)
VD> Response.Write("Товар: <b>" + item.Name + "</b>")
VD> else
VD> Response.Write("Товар: " + item.Name);
VD>
Да, но будет некая потеря (мелкая конечно) в производительности, так как в теле цикла каждый раз выполняется проверка на индекс.
Мне кажется просто, что в явном указании есть больший простор для оптимизации.
То есть такой код
Хотя всё это возможно экономия на копейках
VD>Ага, можно. Но опять же вариант с индексом проще и универсальнее ведь он позволит и последний элемент отработать, и четный/не четный и т.п.
Это верно, код универсальнее, зато ключевые слова строже.
А>А нужно это может быть затем, что операция определения пустой список или нет может быть сравнительно дорогой.
Я таких коллекций не знаю. Длинна там еще может вычисляться относительно дорого (т.е. O(n)), но узнать пуста коллекция или нет всегда можно за время O(1).
А>То есть вызов метода, по сравнению с заведением локальной булевской переменной на стеке А>Типа заворачивания в макрос подобного кода. А>
А>Конечно, с некоей оптимизацией, чтобы присваивание было не в каждой итерации, а только в первой.
С точки зрения производительности тут разницы не будет.
VD>>Ясно, но это не дает видимого преимущества. При этом программисты (даже те кто не использует эту фичу) будут вынуждены учить все эти прибамбасы в макросах. Так что я бы сказал — овчинка выделки не стоит. А>Ну это зависит от того, насколько просто добавить.
Это без относительности простоты добавления.
А>К тому же как я предложил, можно сделать новый макрос, try_foreach, по аналогии с TryCast, TryParse А>То есть тем кто использует foreach все эти фишки учить не надо
Мы говорим о стандартной библиотеке макросов языка. Это почти одно и тоже что синтаксис языка без макросов. Мне кажется, что подобные штуки тут не уместны. В какой-нить библиотеке-расширении еще куда не шло. Но язык в базе свой должен быть чист и интуитивно понятен. Всякие try_foreach это уже перебор. О нем все равно придется знать тем кто пишет на этом языке.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Аноним, Вы писали:
А>Там был on_one, а не on_first. Можно вместо on_one использовать on_non_empty или on_full_list.
Вот, вот. И получается что людям придется изучать все эти лингвистические тонкости. А это уже не хорошо. foreach хорош тем, что он очень прост как в запоминании, так и применении. Его расширения должны быть интуитивно понятны и легки в запоминании. Иначе язык прерваться в очередной Личп, который крут, но большинству людей не понятен.
VD>>В любом случае тоже самое можно реализовать сегодняшними средсвами практически не теряя в выразительности и скорости: VD>>
VD>>foreach(item with index in lst)
VD>> if (index == 1)
VD>> Response.Write("Товар: <b>" + item.Name + "</b>")
VD>> else
VD>> Response.Write("Товар: " + item.Name);
VD>>
А>Да, но будет некая потеря (мелкая конечно) в производительности, так как в теле цикла каждый раз выполняется проверка на индекс.
Дык все равно при реализации какие-то проверки будут. К тому же проверка целого числа — это практически бесплатно. Особенно в сравнении с аналогичным применением функций высшего порядка и тем более Linq-а.
А>Мне кажется просто, что в явном указании есть больший простор для оптимизации. А>То есть такой код А>
А>можно прямо в макросе развернуть, во что-то вроде
Да, несомненно — есть. Но это уже ловля блох. Реального ускорения приложения от этого не будет. Это будет пара выигранных тактов на фоне миллионов тактов приходящихся на само тело цикла. Если же мне захочется сгенерировать очень оптимальный код, то я скорее всего обойдусь рекурсивными функциями, т.е. вообще без макросов. Получится не так компактно, но зато гарантированно эффективно. Макрос же — это удобный и компактный синтаксис при близкой к идеальности производительности.
А>
Ага. По сравнению с list.GetNext() (точнее там будет MoveNext и Current, но это детали) проверка индекса не будет стоить ровным счетом ничего. Другое дело что при доступе к массивам можно сгенерировать более эффективный код. Но там придется проверять тот же самый индекс, и разница опять же не будет.
VD>>Ага, можно. Но опять же вариант с индексом проще и универсальнее ведь он позволит и последний элемент отработать, и четный/не четный и т.п. А>Это верно, код универсальнее, зато ключевые слова строже.
Ну, тут важен баланс. Я считаю, что если приемлемый результат достигается более универсальными методами, то это предпочтительный вариант.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, _nn_, Вы писали:
ИД>> то есть они делают совсем не то, что в питоне делают for-else (else-тело выполняется, если цикл не завершился break-ом) и while-else (выполняется, если цикл завершился по false == условие цикла), а выполняют else-тело, если основное тело не было вызвано ни разу. Все три варианта поведения мягко говоря не интуитивны. Как минимум, в документации должно быть отражено отличие от python.
__>Насчет break-ов действительно не подумал. __>Доделать не сложно
Что-то я поглядел на все это (почитал о питоновской реализации, поспрашал питоновцев
и пришел к выводу, что в таком виде в каком эта фича есть в питоне нам (в Немерле) она не нужна.
Вот в исходном виде, то есть когда otherwise рассматривается как "вместо цикла" это еще куда не шло, хотя и не особо необходимо. А в таком виде как в питоне она будет создавать больше вреда, чем полозы, так как будет приучать людей к императивной обработке списков.
Так что предлагаю или вовсе выкинуть это расширение из стандартной библиотеки, или оставить твой исходный вариант (но с ключевым словом otherwise).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>В любом случае тоже самое можно реализовать сегодняшними средсвами практически не теряя в выразительности и скорости: VD>
VD>foreach(item with index in lst)
VD> if (index == 1)
VD> Response.Write("Товар: <b>" + item.Name + "</b>")
VD> else
VD> Response.Write("Товар: " + item.Name);
VD>
Может тогда сделать несколько переменных с именем производным от переменной цикла?
foreach (item in lst with vars)
{
if (itemIsFirst || itemIsLast)
Response.Write("Товар: <b>" + item.Name + "</b>");
else if (itemIndex % 2)
Response.Write("Товар: <i>" + item.Name + "</i>");
else
Response.Write("Товар: " + item.Name);
}
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, _nn_, Вы писали:
__>>Первоначальный вариант был неправильным как подмеченно здесь http://rsdn.ru/forum/nemerle/3892867.1.aspx
. __>>Я начал исправлять и наткнулся на проблемы.
VD>Вот можно по подробнее об этих проблемах. А то я что-то не догоняю.
Поведение Python это вызов блока else всегда если не было break.
Сперва, спохватившись подумал, что надо менять как там, но все больше склоняюсь к оригинальному варианту как сейчас.
Т.е. otherwise вызывается если не было действия в цикле.
Здравствуйте, _nn_, Вы писали:
__>Поведение Python это вызов блока else всегда если не было break.
__>Сперва, спохватившись подумал, что надо менять как там, но все больше склоняюсь к оригинальному варианту как сейчас. __>Т.е. otherwise вызывается если не было действия в цикле.
В общем, мое мнение — питоновская реализация не понятна интуитивна, мало применима на практике, навязывает императивный стиль программирования и не соответствует ключевым словам else / otherwise (особенно последнему). Так что лично я против введения ее в немерловую библиотеку макросов.
Если хочешь можешь оставить свой первоначальный вариант, но с ключевым словом otherwise, в котором otherwise означает "вместо цикла" (вызванивается только тогда когда тело цикла не разу не выполнилось и не произошло исключения).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, _nn_, Вы писали:
__>Сперва, спохватившись подумал, что надо менять как там, но все больше склоняюсь к оригинальному варианту как сейчас. __>Т.е. otherwise вызывается если не было действия в цикле.
Согласен. Тогда остается только один вопрос. А нужен ли вообще этот else? Будет ли он реально использоваться?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.