Пишет Honeyman ([info]honeyman)
@ 2005-09-24 19:52:00
Previous Entry  Add to memories!  Next Entry
Метки данной записи:python, программирование

Про Питон
С изучением Python-а дела складываются не слишком радостно. В ходе изучения нашёл там такую фишку, что сразу задумался, стоит ли мне дальше тратить время на Python, если там такое. Почесав голову, обнаглел и решил спросить прямо в рассылке у девелоперов. После не слишком долгой и слегка нервной переписки выяснил, что они это действительно считают нормальным и исправлять никак не собираются. Более того, со слов Самого Гвидо Ван КенобиРоссума выяснилось, что это появилось в Python-е потому, что так было проще всего реализовать

Сижу сейчас, размышляю, что учить вместо Python-а. Ruby (upd: говорят, там так же)? Erlang? А то и Lisp, на который руки уже давно чешутся?

А, да. Про найденную фишку…

Представьте себе, что в Python-е циклы (for, while) и условия (if), а также их представления в виде list comprehensions (это такие чрезвычайно удобные формы создания списков в Python-е, при которых создаётся реальный (а ещё можно «ленивый» с помощью generator expressions, отличающихся по синтаксису от list comprehensions исключительно видом скобок) список по заданному выражению, определяющему его элементы; например, список из квадратов первых десяти натуральных чисел можно задать как [a**2 for a in range (1, 11)]) не создают нового подпространства имён!

Что, непонятно? Это значит всего лишь то, что переменная цикла после завершения цикла не умирает и продолжает существовать дальше, со значением, которое она приняла в последней итерации цикла. Написали два цикла по разным переменным — дальше в функции или коде живут (и путаются под руками) две переменных, n+1 цикл — n+1 переменная…

Лично я столкнулся с этой проблемой, когда примерно полчаса пытался понять, почему в цикле для разных значений переменной цикла печатаются одинаковые значения одной функции, которая принципиальноодинаковой быть не может. И только через полчаса обратил внимание, что я переменную цикла уже давно переменовал с i на imsi, но внутри цикла, в месте вызова злополучной функции, переименовать забыл, а транслятор мне об этом не подсказал, потому что незадолго до этого цикла я делал другой цикл по переменной i, после чего транслятор посчитал, что переменная i уже определена…

Поняв это, я схватился за голову и стал пробовать все возможные глупости, которые можно сделать при наличии этой «фичи». И все глупости сработали!

Вот несколько примеров, оформленных в виде готового и пригодного к использованию исходника на Питоне:


#!/usr/bin/env python -t
# -*- coding: CP1251 -*-

# Создадим подопытный цикл
a1 = ['a', 'b', 'c', 'd', 'e']

# То, с чего я начал: видимость переменной цикла за пределами цикла
for k in a1:
    pass
    k = None
# никакой ошибки по причине неопределённой переменной в следующей строке
# не будет ибо переменная k считается определённой и равной последнему
# значению цикла
print "k: %s" % k

# Ещё более страшное: видимость переменной list comprehension-а за его
# пределами, делающая чрезвычайно опасным банальный копипаст его
b1 = [l for l in a1]
print "l: %s" % l # переменная l тоже определена

# Если в list comprehension-е несколько вложенных циклов,..
# это не имеет значения, все их переменные будут видны снаружи
c1 = [(m, n) for m in a1 for n in b1]
print "m: %s, n: %s" % (m, n) # m и n вполне себе определены

# Даже переменные из comprehension-а внутри comprehension-а
# "протекают" в область видимости!
d1 = [o for o in [p for p in a1]]
print "o: %s, p: %s" % (o, p) # даже p определён!

# Что там: если мы просто определим переменную внутри цикла,
# она тоже утечёт наружу
for e1 in a1:
    q = 'Test'
print "q: %s" % q # q определён - страшно?

# А теперь барабанная дробь...
for s in a1:
    print "s0: %s, " % s,
    f1 = [s for s in b1]
    # А вот достучитесь теперь до переменной s из цикла по списку a1!
    print "s1: %s" % s


Я думаю, не стоит объяснять и демонстрировать, что изнутри for-ов и if-ов переменные точно также «вытекают» наружу. Вытекают, уж поверьте. Нереально засоряя пространство имён основного кода и делающего чрезвычайно вероятными (а они ещё будут и чрезвычайно сложно уловимыми) ошибки, подобные моей — когда используется переменная, которая по привычке после других, более приличных языков, кажется уже деинициализированной.


Но и это ещё не самое забавное. Есть такой популярный класс языков — функциональные языки программирования. И апологеты таких ругаются на наличие в обычных языках… переменных. Мол, переменные плохи тем, что они меняют своё значение в течение работы программы, что может вызвать неприятные сайд-эффекты, когда ты пользуешь не то значение, которое хотел. Мол, в одном и том же месте программы при типичном использовании языка (т.е., безо всяких хакерских триков) одна и та же переменная может принимать различные значения. Программистам на других языках это уже должно быть привычно,… но если вдуматься, это на самом деле нелогично.

Я ещё не настолько матёр, чтобы обходиться без переменных вообще, поэтому бесстыдно пользуюсь ими. Но в случае с Python-ом всё ещё страшнее.

Взгляните на следующий код:
if a == b:
    j = f(7)
print j

Тут у переменной j в момент попытки печати может быть не только неясное/плавающее значение, задаваемое функцией f()у неё может быть плавающий статус определённости!


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

А благодаря последнему примеру с неопределённым статусом существования, у меня теперь с Python-ом есть чёткая и красочная ассоциация. Тоже «животная», кстати. Кто догадается, какая ;) ?


(Добавить комментарий)


[info]sighup
2005-09-24 11:39 pm (local) (ссылка)
Да, это известная misfeature.

(Ответить)


[info]_radiant_
2005-09-25 08:21 pm (local) (ссылка)
Хех, спасибо за перевод комментариев ;)

Теперь после-асечное:
1. Все примеры, окромя последнего, меня не только не страшат, но и не удивляют.
Более того, использование последнего значения переменной цикла -- нормальный side-effect.
Понятно, что ты после c(++)/perl к этому не привык, а я так работаю :)

А про последний (после "барабанной" дроби). Ну ты ведь сделаешь втык подчинённому, который на c напишет:
for(int i=1; i<=10;i++){
   for (int i=2; i<=6; i++) {
     // bla-bla
   }
}

Тут же то же самое, просто записано по-другому.

И про "копи-паст" повторю: за его использование [без, как минимум, минутных раздумий] программисту руки отрывать надо.

P.S.: Ты вот это видел? http://www.norvig.com/python-iaq.html

(Ответить)(Ветвь дискуссии)


[info]_radiant_
2005-09-25 08:26 pm (local) (ссылка)
Последний код (с т.н. "плавающим статусом определённости") тоже не удивлён.
В общем, это выглядит, как мой будущий пост по поводу новой работы: "люди, вы представляете, они их [программы] компилируют!" ;)

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]honeyman
2005-10-01 07:07 am (local) (ссылка)
> Последний код (с т.н. "плавающим статусом определённости") тоже не удивлён.
Если смотреть на него, будучи привычным к отсутствиям локальных ограничений видимости - действительно, ничего интересного.
А если смотреть на него, хорошо повозившись с кодом, имеющим повышенные требования к надёжности...

Кстати, загадку, с чем у меня теперь Питон ассоциируется благодаря этим багам (особенно последнему), разгадал :) ?

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]_radiant_
2005-10-18 07:58 pm (local) (ссылка)
выношу из логов аськи:
мой вариант -- землянной червяк.

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

(Ответить)(Уровень выше)


[info]honeyman
2005-10-01 07:04 am (local) (ссылка)
> нормальный side-effect.
Полностью согласен. Нормальный сайд-эффект серьёзной проблемы в надёжности.

> Понятно, что ты после c(++)/perl к этому не привык, а я так работаю :)
Кому и коб После приличных языков для меня это совершенно чуждо. И непонятно - уровню языка не соответствует. Как если бы в том же Питоне или Перле память вручную выделять и освобождать - одной степени опасности особенности. Хотя некоторые и с malloc-ом живут и не жалуются ;)

> Тут же то же самое, просто записано по-другому.
А вот нифига. Если ты повнимательнее посмотришь на мой и на свой примеры, то увидишь, что твой пример - это блок кода. А мой - это выражение. Надеюсь, дальше объяснять не надо.

> И про "копи-паст" повторю: за его использование [без, как минимум, минутных раздумий] программисту руки отрывать надо.

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

> P.S.: Ты вот это видел? http://www.norvig.com/python-iaq.html
Теперь да :)

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]_radiant_
2005-10-18 08:11 pm (local) (ссылка)
1. я именно _только_ про "копи-паст" и говорил.
3. а. в чём проблема? выносим в функцию, которую пять раз вызываем.
б. вызываем её на другом "наборе значений"
2. не согласен.
Случай, когда раздумываем делать или не делать "копи-паст" сродни хорошей практике создать дизайн, перед тем как начать кодить :)
Что характерно, одна и та же причина: избежать геморроя во время поддержки.

Во-вторых, я не понял, какой можно "перекладывать задачу по раздумьям на компьютер". Причём тут копи-паст?
+ есть более важные сферы экономии времени программиста, чем двух минут раздумий.
Дизайн перед кодингом.
Мотивация. Банально -- нормальное рабочее место. [ну что поделать, продуктивность действительно очень зависит от состояния программиста]

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]honeyman
2005-10-18 10:48 pm (local) (ссылка)
> 1. я именно _только_ про "копи-паст" и говорил.
Ещё раз акцентирую: описанная мной проблема характерна не только и не столько для "копи-паста", но для "кут-н-паста". И вообще, она заставляет лишний раз думать о несущественных деталях, отвлекая мозги программиста от более важных задач.

> 3. а. в чём проблема? выносим в функцию, которую пять раз вызываем.
Некоторые паттерны либо слишком малы, либо слишком общи, чтобы выносить их в отдельную функцию. Ну на кой ляд делать "универсальный итератор по массиву" с передачей в него обработчика каждого элемента только для того, чтобы спрятать переменную цикла? Особенно в том случае, когда не прячущий переменную цикла итератор предусмотрен языком?
Нее, это не Lisp, определённо.

> 2. не согласен.
> Случай, когда раздумываем делать или не делать "копи-паст"
В моём случае это задача такого же минимального приоритета, как раздумывание, как назвать переменную цикла. Ты просто подумай: я скопипастнул кусок кода, отвечающий ЗА ИТЕРИРОВАНИЕ ПО ЦИКЛУ!
По сути, я скопипастнул всего лишь фразу for (int i = 1; i < arr.length(); i++) - и даже ЭТО привело к ошибкам.
А мог и не скопипастнуть, кстати. Мог и напечатать.

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

> Во-вторых, я не понял, какой можно "перекладывать задачу по раздумьям на компьютер".
Когда я пишу программу, я думаю о программе в целом, а не о каждой отдельной детали.
Я "перебираю все элементы массива". А не "делаю цикл по хранилищу list с присвоением текущего значения переменной i на каждом шаге". Детали - это проблемы компьютера.
То, что меня при этом интересует - пройти по всему массиву. То, что меня не интересует (а если интересует, то только постольку поскольку, не как самоцель) - как будет зваться переменная, которая будет обрабатываться на каждом шаге. Она - это всего лишь временное промежуточное явление, существующее с момента перехода на обработку нового значения и до момента использования. Она - это примерно как write-once-константа в функциональных языках.

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]honeyman
2005-10-18 10:53 pm (local) (ссылка)
Сейчас поясню, если непонятно: в SML, с которого я начал знакомство с функциональными языками, переменных нет (ну, или по крайней мере нет на поверхности; может, где-нибудь внутри для грязных хаков они и есть,... но мне они не понадобились).
Как же это можно - без переменных-то? А вот так. Не нужны они в принципе. Нас интересуют входные значения и интересуют выходные значения функции, и любое значение любой переменной определяется некоторой функцией - а раз так, то почему бы нам не вызвать эту функцию каждый раз, когда нам нужна эта "переменная"?
С другой стороны, в SML есть "константы" (там они называются, кажется, "биндинги"). Правда, определяемые не в compile-time, а в run-time. Т.е., такие константы, которые создаются один раз в любой момент и с той поры больше не меняются.
И этого, в принципе, достаточно.
Потому что никому, в общем-то, не нужно настолько сильно, чтобы значение в этой константе менялось. Его нужно просто вычислить и просто заиспользовать. Записали в константу, чуть поработали, прочитали из константы. Всё, константа больше не нужна, её можно стирать. Второй раз в неё писать уже не надо - второй раз уже можно писать в ДРУГУЮ константу. Чтобы не путаться, для чего использовалось значение из первой.
Т.е., фактически основной паттерн использования такой константы - это:

val myconst = f1(myparms);
...
someotherresult = f2(myconst);

Пишем, читаем, больше не перезаписываем.
Для чего может быть нужно именно создание константы в подобном случае, а не дёрганье f1 в тот момент, когда нужно значение такой "константы"? - для упрощения выражений (чтобы там не было пятнадцатиуровневых вложений скобок, как в Лиспе каком, и чтобы одно сложное выражение разбилось на выражения от более высокоуровневых компонент) и для большей читабельности. Ну, и особенно для тех случаев, когда мы пишем один раз, а читаем несколько раз.

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

Так вот, к чем это я? А к тому, что в таком паттерне очень хорошо прослеживается тот факт, что такая "переменная" совершенно не нужна до момента первоначальной записи в неё и после момента последнего чтения из неё.
Но это всё в качественных и продуманных языках. В тех же, в которых переменные позволяют перезаписывать значения в них, такое поведение приходится эмулировать. Чтобы переменная не мешалась под ногами после того момента, как её последний раз прочитали, её время жизни приходится явно - руками - ограничивать. Например, в C/C++/Java/C#, да и даже в Perl-е я прямо внутри функций ставлю дополнительные блоки, вся суть которых - только в ограничении области видимости используемых в них переменных. По сути, это функция в функции (ну зачем выносить код в отдельную функцию, если она используется только один раз?) Пример:
sub countmass($)
{
  {
    my $object = shift; // начали использовать $object
  
    my $volume;
    {
      my @dimensions = ($object.width, $object.height, $object.depth);
      $volume = reduce (multiply, @dimensions);
    } // Начали использовать $volume, перестали использовать @dimensions

    my $density;
    {
      my $material = $object.material;
      $density = %DensityTable{$material};
    } // Записали в $density, перестали использовать $material.
  } Перестали использовать $object

  my $mass = $volume * $density;
  return $mass; // Перестали использовать $mass
}


Такое ограничение видимости (посредством записи в одну переменную только один раз) спасает как раз от таких проблем, которые со мной случились - когда переменная используется не по своему назначению. Но в Питоне это как раз невозможно...

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]honeyman
2005-10-18 10:53 pm (local) (ссылка)
Кстати, такая методика отслеживания состояния переменной как представление переменной как write-once константы очень интересно применима к циклам в случае с переменной цикла, ограниченной в видимости только внутри цикла, и с переменной, видимой снаружи. Сразу видно, что в первом случае в каждой итерации используется фактически одна-единственная независимая "константа". Во втором случае, столь элегантного подхода не видно.

(Ответить)(Уровень выше)


[info]_radiant_
2005-10-19 03:59 pm (local) (ссылка)
Если коротко повторить, то что уже изложил в аське:
Каждый язык автоматически тянет за собой шаблоны разработки кода.

Л.О: {
Мне сейчас, когда участвую в топкодере и пишу на Java, с которой знаком поверхностно -- это особенно заметно. Не хватает шаблонов. Теряю минут десять создавая то, что есть в либах, или минут двадцать, ища непривычную ошибку. Но это _мои_ проблемы, а не языка. _Я_ не привык к языку. _Я_ не создал себе шаблоны кодинга.
Я и профессионал Java один и тот же алгоритм будем реализовывать полчаса и две минуты, соотвественно. И его код будет скорее всего безопаснее.
}

Так вот, когда изучаешь новый язык ты вырабатываешь новые шаблоны.
Ты хотел применить шаблоны из SML, C++ в Питоне, и обломился. Ну и что?
Можно вырабатать новый шаблон: "не использовать переменную два раза. при _копи-пасте_ заменить все названия переменных или убить себя об стенку".
Если оный шаблон напрягает, ок, язык не _для тебя_. И всё.

И чем шаблон SML (без переменных) лучше других я не вижу.
"это фактически всё, что нам необходимо от переменных. Остальное - не просто ненужные, но вредные фенечки."
Список остальных фенечек с доказательством вредности можешь предоставить?
Без этого процитированное только твоё _мнение_, и не более того...

(Ответить)(Уровень выше)


[info]_radiant_
2005-10-19 04:06 pm (local) (ссылка)
1. Не понимаю. Покажи пример для "кут-н-паста"

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]honeyman
2005-10-19 09:42 pm (local) (ссылка)
Кут-н-паст лист компрехеншна внутрь какого-то цикла.

Обрати внимание, что лист компрехеншн - это не statement block, а expression. Значит, по определению ощущается как безсайдэффектный...

(Ответить)(Уровень выше)


[info]_radiant_
2005-10-19 04:08 pm (local) (ссылка)
2. в обоих случаях (скопировал, написал) сам виноват.
Ну действительно -- скопировал -- будь молодцом, поменяй переменные.

(Ответить)(Уровень выше)


[info]_radiant_
2005-10-19 04:09 pm (local) (ссылка)
3.
я не вкурил, что ты говорил только про _объявление_ цикла.
ест-но, его никто не выделяет в функцию.
а в этом случае, см. пункт 2.

(Ответить)(Уровень выше)


[info]_radiant_
2005-10-19 04:12 pm (local) (ссылка)
4. угу. конечно, тебя не интересует как именно что-то будет называться.
Здесь уже работает "шаблон кодинга".
Вот таки поверишь ты, что у меня описываемая проблема (взять объявление, поменять) занимает порядка полминуты (максимум). И при этом "мозг не задет" (ц) -- он продолжает думать о более высокоуровневых деталях.

(Ответить)(Уровень выше)


[info]honeyman
2005-10-18 10:54 pm (local) (ссылка)
Так вот, к чему я это рассказывал? К тому, что сами по себе данные - несущественны. Размышления о структурах данных - это низкоуровневый подход (ассемблер прямо - "гоняние байтиков"), размышления об алгоритмах - высокоуровневый.
Тем более, нам не важны такие мелочи, как имена временных переменных. Хоть горшком их назови, лишь бы использовать просто было, и проблем не возникало.
В случае с Питоном, нам надо думать о каждой переменной в каждом цикле (да и не только цикле) - как она называется, и не пересекается ли она по имени с какой другой переменной. И проблема в том, что пересекаться с другими переменными в Python-е она имеет больше возможностей. Больше, чем надо.

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

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]_radiant_
2005-10-19 04:01 pm (local) (ссылка)
Неа, не чувствую.
Время "потерь" убывает. Первый раз две минуты, потом полторы, а потом десять секунд при копировании...
Такое понравившееся мне слово "шаблоны"...

(Ответить)(Уровень выше)


[info]_radiant_
2005-10-19 04:05 pm (local) (ссылка)
"Больше чем надо" на сколько?
Большинство функций относительно малы — отслеживание пересечений не составляет труда.
Больших функций гораздо меньше (и на них по умолчанию внимание нужно уделять).

Повторяю: я на (не самом лучшем языке) php с этим работаю. Проблем с отслеживанием нет...

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]honeyman
2005-10-19 09:41 pm (local) (ссылка)
> отслеживание пересечений не составляет труда.
Оно требуется. Это уже перебор.

> Проблем с отслеживанием нет...
Привык спать днём, а в 5 утра сидеть в интернете ;) ?

> Больших функций гораздо меньше (и на них по умолчанию внимание нужно уделять).
Там найдётся и помимо этих мелочей, на что уделять внимание человека. Для подобного же слежения хватило бы внимания компилятора.

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]_radiant_
2005-10-21 02:41 pm (local) (ссылка)
>> Проблем с отслеживанием нет...
> Привык спать днём, а в 5 утра сидеть в интернете ;) ?

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

Вот ты говоришь, то о чём думаешь, но долго не пробовал. Тебе кажется, что это действительно большая проблема.
Вот если бы ты проработал долгое время вынужденно, а потом сказал, что мешало кодить, и вот "если бы [...], то я бы сделал на 30% быстрее, т.к. тратил кучу времени на дебаг этой проблемы", etc. — тогда бы я понял.

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]honeyman
2005-10-21 03:21 pm (local) (ссылка)
Опля, это похоже на "отражение" :)

Ибо я как раз собирался тебе сказать... вот смотри, ты активно защищаешь такие "переменные с неограниченной видимостью". И ты имел достаточно богатый опыт работы с PHP, в котором такие переменные - все. А имел ли ты достаточно богатый опыт работы с языками, в которых область видимости переменных ограничивается? Perl (с use strict), C/C++/Java/C#?

Тут я хотел ввернуть умную фразу про "Гёделеву неполноту"... но подумал и посчитал, что, раз мы пришли к одной и той же мысли с разных концов, то является неочевидным, какой же именно подход является надмножеством которого. Поэтому...

Рассмотрим оба подхода, с "ограничением переменных как в С++" и "ограничением переменных как в Python", и посмотрим, какой же подход реализуется средствами другого. И достаточно быстро увидим, что "ограничение переменных, как в Python" (вместе с сайд-эффектом от сохранения значения переменной цикла по завершению цикла) достаточно легко эмулируется посредством явного создания переменной до начала цикла:
#include 

int main(int argc, char* argv[])
{
	int i;
	for ( i = 5; i < 10 ; i++ )
		std::cout << i << std::endl;

	std::cout << "Fin:" << i << std::endl;
	exit (EXIT_SUCCESS);
}


Аналогично можно сделать в том же Перле.

С другой стороны, для эмуляции локальной ограниченности при её отсутствии в явном виде в Питоне... средств нет. На что я и плююсь. А Ruby-вский трик с лямбдой не сработает: лямбда в Питоне позволяет указать только expression, а не statement.

Делаем выводы: отсутствие локальной ограниченности хуже объективно :)

PS А если тебе личного мнения хочется - то повторю: я пользуюсь локальными ограничениями видимости давно и сознательно. Примерно как DbC-образными проверками во всех подобающих местах (хотя бы на уровне assert-а). Программу, в которой переменная продолжает существовать и путаться под руками после того момента, как я перестал ею сознательно пользоваться и следить за её корректностью и актуальностью - и я ничего с этим не могу поделать - я надёжной и безопасной считать не могу.

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

Кстати, я её не ответил на твой коммент на http://www.livejournal.com/users/honeyman/37474.html?replyto=103266 - руки никак не дойдут. Там эта тема будет ещё чуток подробнее раскрыта.

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]_radiant_
2005-10-21 04:38 pm (local) (ссылка)
Ну собственно, это я и добивался :)
Перехода от эмоции к объективным фактам — это интересней.

1. Вот. Опыт имел (c++, c#, java), но не богатый.
В связи с переходом становится интересней, как это :)
[ЛО: Но заметь, явно больше чем ты с Питоном :Р ]

2. Аргумент один: в c-like можно эмулировать "глобальное" поведение, в p.+ нельзя эмулировать "локальное" поведение.

Смешной ответ: Можно. В php точно. unset($a)


А теперь останов. Вдох. Размышляем.
А зачем эмулировать?
Вот есть функция. В ней какие-то переменные. Двух типов:
1. переменные, которые содержат данные.
2. переменные циклов.

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


Плюс ещё такой момент. Вот сделал два цикла, внутри какие-то переменные инфу накапливаются. Сделал ты их копи-пастом :) Поэтому имена совпадают.
Потребовалось взять накопленную инфу из обоих циклов. Выносишь объявления. И...
А! Нужно имена поменять во втором цикле! Блин! Это же целых пять минут может занять!! Как ужасно!...


Ну а если честно, мне кажется фигню обсуждаем.
Мне интересней обкатывать какие-то идеи и неочевидные вещи в проектировании (причём если их сейчас "проморгать", то появится геморрой при maintain`е), чем пятиминутные проблемы.
Это я к тому, что устал :)

(Ответить)(Уровень выше)


[info]kililili
2005-09-27 02:52 pm (local) (ссылка)
код читать лениво, но с переменной в цикле очень весело :)

(Ответить)


[info]_nik_
2005-09-27 11:32 pm (local) (ссылка)
> Сижу сейчас, размышляю, что учить вместо Python-а.

А я тебя пердупреждал ;o)

> Ruby (upd: говорят, там так же)?

А чего ОБС слушать? Возьми да проверь. Делов то на две команды.

Лично мне руби понравился. И егойная книжка "прагматичное программирование" -- тоже, хотя и недочитанной осталась. Ибо решил, что такой уровень ООПа выглядит красиво, но лично мне особо не нужен (угу, вчера и профессор Вирт нам сообщил, что разделяет это мнение, а он как-никак в ЯПах разбираться должОн).

> Erlang? А то и Lisp, на который руки уже давно чешутся?

Первый не советую... Хотя... В общем -- скорее всего разочаруешься, как с питоном, но язык забавненький.
Лисп -- несомненно полезен. И уж при эго возрасте "ошибок роста", типа как в питоне, содержать не может.
Лично я хочу за оный лисп (в его clisp, e-lisp и shema инкарнациях) взяться, когда время свободное проявится , что, впрочем, в связи с наличием хоббийный проектов не очень скорое событие :o(...

> у меня теперь с Python-ом есть чёткая и красочная ассоциация. Тоже «животная», кстати. Кто догадается, какая ;) ?

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

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

Кстати -- а догадаешься ли ты о главном бенефите оной свободы?

(Ответить)


[info]madfire
2005-10-21 01:12 am (local) (ссылка)
я не знаю шо вы там курите

berkus@mindgap:~> irb
irb(main):001:0> a = [1,2,3,4]
=> [1, 2, 3, 4]
irb(main):002:0> a.each { |f| p f }
1
2
3
4
=> [1, 2, 3, 4]
irb(main):003:0> p f
NameError: undefined local variable or method `f' for main:Object
from (irb):3
irb(main):004:0>

(Ответить)(Ветвь дискуссии)


[info]honeyman
2005-10-21 01:40 pm (local) (ссылка)
(Мрачно) Питон мы курим, Питон. А не Руби.

Хотя покурить Руби, похоже, тоже стоит.

(Ответить)(Уровень выше)


[info]buriy
2005-10-22 08:11 pm (local) (ссылка)
что за бред у тебя тут написан?
на слишком большие возможности языка что-ли ругаешься? :)

вариант 1 "спасения" от лишних переменных. использовать процедуры. там сайд-эффектов не будет, если ты их добровольно не допустишь.

вариант 2. как удалять переменные? командой delete! раз-раз, и переменных нету!

вариант 3. забить. от наличия ненужных с твоей йебанутой точки зрения переменных еще никто не умирал. учись мыслить в терминах ДИНАМИЧЕСКИХ неймспейсов. это тебе не паскаль, где все переменные нужно определять перед именем.
вариант 4. завести пустой класс в который складывать лишний геморрой.
вариант 5. использовать классы (и, если сильно надо, __slots__ или метаклассы)
вариант 6. идти в Бабруйск с плакатом "Петон ниасилил".

резюмируя, ДИНАМИЧЕСКИЙ неймспейс в питоне - это ФИЧА, а не недостаток, исправляемый размашистым движением косы.

По поводу ФЯ. ну да, нет в них переменных. ну и плюс это или минус?
Вот дядя Вирт считает это не плюсом, а скорее мозгоёпством, "academic excersize", как он говорит, объясняя, что текущие компьютеры все равно используют память для хранения промежуточных и конечных результатов (то есть получается пользуются переменными), даже если язык программирования эти переменные в явном виде не использует. И в этом я с ним полностью согласен.

(Ответить)(Ветвь дискуссии)


[info]buriy
2005-10-22 08:12 pm (local) (ссылка)
вариант 3. забить. от наличия ненужных с твоей йебанутой точки зрения переменных еще никто не умирал. учись мыслить в терминах ДИНАМИЧЕСКИХ неймспейсов. это тебе не паскаль, где все переменные нужно определять перед употреблением.

(Ответить)(Уровень выше)


[info]honeyman
2005-10-22 08:23 pm (local) (ссылка)
> на слишком большие возможности языка что-ли ругаешься? :)
Да нет, на недостаточные. Объяснить, или сам догадаешься?
(Хотя я где-то в комментах уже объяснял).

> вариант 1 "спасения" от лишних переменных. использовать процедуры. там сайд-эффектов не будет, если ты их добровольно не допустишь.

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

> вариант 2. как удалять переменные? командой delete! раз-раз, и переменных нету!
Неплохо. Почти как деструктор у класса вручную вызывать. Ага, или free после malloc-а.
(O(n) при изменении размера программы. Мило, мило.)

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

> вариант 4. завести пустой класс в который складывать лишний геморрой.
> вариант 5. использовать классы (и, если сильно надо, __slots__ или метаклассы
Ещё интереснее. Вместо функции на каждое использование переменной класс заводить.

> вариант 6. идти в Бабруйск с плакатом "Петон ниасилил".
Так и придётся, судя по всему. Что поделать, тяжёл для меня Питон, слишком высокие у него требования к программисту. Пойду писать на чём-нибудь более простом. На С++ хотя бы.

> резюмируя, ДИНАМИЧЕСКИЙ неймспейс в питоне - это ФИЧА, а не недостаток, исправляемый размашистым движением косы.

О как.
Что интересно, сайд-эффекты этой "фичи" прекрасно эмулируются в С++-образных языках. Обратное неверно.

> Вот дядя Вирт считает это не плюсом, а скорее мозгоёпством, "academic excersize", как он говорит, объясняя, что текущие компьютеры все равно используют память для хранения промежуточных и конечных результатов (то есть получается пользуются переменными), даже если язык программирования эти переменные в явном виде не использует. И в этом я с ним полностью согласен.

Да-да. Поэтому писать надо обязательно на языках, максимально близких к архитектуре.
Например, на ассемблере. Более высокие абстракции - это мудрствования и заумь ненужные. Я правильно понял?

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]buriy
2005-10-23 04:56 pm (local) (ссылка)
>Ну что поделать, паранойя у меня. Вызванная опытом программирования с повышенными
>требованиями к надёжности.
На скриптовых языках с GC нельзя писать программы для ядерных реакторов. Это должно быть понятно даже параноикам. При написании таких высоконадежных программ должна быть произведена математически корректная верификация программы. Да и деньги за такие программы платят совсем другие.

>> резюмируя, ДИНАМИЧЕСКИЙ неймспейс в питоне - это ФИЧА, а не недостаток, исправляемый размашистым движением косы.
>О как.
>Что интересно, сайд-эффекты этой "фичи" прекрасно эмулируются в С++-образных языках.
>Обратное неверно.
Я видел то твое объяснение. Ты не прав. Вот твой пример (чуть доделанный, хотя можно было оставить и так, как было):
if a == b:
    j = 5
try:
    print j
except:
    print "No j"

Это поведение не воспроизвести в C++-образных языках со статическими (стековыми) неймспейсами. Эти варианты ортогональны.
Далее, ты раздул из мухи - возможности получения последнего значения f в выражении a = [f for f in (1,2,3)]; - слона. Интересно, программировал ли ты когда нибудь на C, где нет возможности написать for(int i=0; i<10; i++){...} ? Что, ты каждый цикл заключал в фигурные скобки или может быть смирялся с появлением переменной i в неймспейсе?

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

Вот еще тебе один вариант.
The unanimous response to my question "Why no 'use strict' in Python?" was: PyChecker.
Я не знаю, исправит ли твои шаловливые ручки PyChecker... я им до сих пор так ни разу и не воспользовался...

Объясни мне, почему мне за полгода программирования на Python эта ошибка ни разу не встретилась?
Ты привык к полному контролю в C и C++. В Python сила не в этом. Сила в динамике. Динамика тоже может быть контролируемой, но немного другими средствами.
=====
>> Вот дядя Вирт считает это не плюсом, а скорее мозгоёпством, "academic excersize", как он говорит, объясняя, что текущие компьютеры все равно используют память для хранения промежуточных и конечных результатов (то есть получается пользуются переменными), даже если язык программирования эти переменные в явном виде не использует. И в этом я с ним полностью согласен.

>Да-да. Поэтому писать надо обязательно на языках, максимально близких к архитектуре.
>Например, на ассемблере. Более высокие абстракции - это мудрствования и заумь ненужные.
>Я правильно понял?
Подумай маленько. Вирт придумал Паскаль. Паскаль похож на ассемблер?
Вообще, по мнению проф. Вирта, писать надо на Обероне. Очень симпатичный и компактный язык. Один минус, мне C-like syntax больше нравится, и begin..end писать я не люблю.
Кстати, он читал лекции в Sun перед разработкой там Java. Часть идей поэтому взята у него. Думаешь, Sun сам идею про VM придумал бы? :)
Может тебе Оберон приглянется? Правда, там GUI плохие были, когда я на него смотрел... мож, исправили :)
Еще посмотри D. Это правильно доделанный C, а не так, как в C++. Он, к сожалению, не глючит, и мало навязывается, поэтому фанатов у него мало, не то что у продукции Microsoft ;)

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

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]honeyman
2005-10-24 02:41 pm (local) (ссылка)
> На скриптовых языках с GC нельзя писать программы для ядерных реакторов.
Разумеется.
Про реакторы это была так, гипербола.
Но ведь помимо ядерных реакторов, существует много областей, где возникают повышенные требования к надёжности программы. И недостатки скриптовости и GC при этом несущественны.
Вот, например, как раз то, чем я занимаюсь сейчас - конвертирование данных абонентов одной GSM-сети из формата старой базы в формат новой базы. Количество абонентов, обработанных мною, уже приближается ко второму миллиону. Пока приходится использовать Perl.

> Да и деньги за такие программы платят совсем другие.
Ну что поделать... люблю хорошие деньги :)

> Я видел то твое объяснение. Ты не прав. Вот твой пример (чуть доделанный, хотя можно было оставить и так, как было):
В том плане, что не выяснить на runtime, была ли переменная определена до этого внутри if-а?
Да, согласен - здесь сэмулировать такое поведение нельзя.
Но по моему мнению - и слава богу. Если следить за сайд-эффектами от варьирующихся значений переменных ещё более-менее привычно, то следить за сайд-эффектами от варьирующейся определённости переменных (кстати, догадался, про какую "животную ассоциацию" я писал :) ?) - это уже избыточная нагрузка для моих усталых мозгов...

> Интересно, программировал ли ты когда нибудь на C, где нет возможности
C:\!test>cat test.c
#include <stdio.h>

int main(int argc, char* argv[])
{
    for ( int i =5; i <= 7 ; i++ )
    {
    }
    printf ("%i\n", i);
}

C:\!test>gcc test.c
test.c: In function `main':
test.c:5: error: `for' loop initial declaration used outside C99 mode

C:\!test>gcc -std=c99 test.c
test.c: In function `main':
test.c:8: error: `i' undeclared (first use in this function)
test.c:8: error: (Each undeclared identifier is reported only once
test.c:8: error: for each function it appears in.)

(До стандарта C99 писать for (int i...) действительно было нельзя; по стандарту C99 - уже можно, и переменная за пределами цикла видна не будет.)

Впрочем, честно говоря, я действительно на С писал достаточно мало. Да и на С++ тоже не ахти. Писал на Java, писал на C#, писал на Perl, писал на забавном проприетарном языке Protel, предназначенном для программирования Nortel-евских GSM-свитчей (причём, наверное, на нём писал больше всего) - и везде мог ограничить видимость каждой переменной только тем участком, в котором она реально используется.

> The unanimous response to my question "Why no 'use strict' in Python?" was: PyChecker.
А вот за это искреннее спасибо. Мне на самом деле не хочется отказываться от Python - он слишком симпатичен.

> Что ж, если ты параноик, то используй для list comprehensions те же переменные, что и для циклов. Потому что это те же самые циклы, только записанные другим образом.

Я тут уже где-то писал, что цикл - это statement, а list comprehension - это expression, что подразумевает (хотя вовсе не обязывает, к сожалению) отсутствие сайд-эффектов в list comprehension. Так что это не просто "тот же самый цикл" - это цикл, переведённый в другую синтаксическую конструкцию. Примерно как массив, переведённый в список.

... продолжение следует - здесь оно в коммент не влезло.

(Ответить)(Уровень выше) (Ветвь дискуссии)


[info]buriy
2005-10-24 03:03 pm (local) (ссылка)
>> Я видел то твое объяснение. Ты не прав. Вот твой пример (чуть доделанный, хотя можно было оставить и так, как было):
>В том плане, что не выяснить на runtime, была ли переменная определена до этого внутри if-а?
>Да, согласен - здесь сэмулировать такое поведение нельзя.
>Но по моему мнению - и слава богу.
Вот видишь, ты судишь из своих привычек.
Я говорю, сила питона в динамике. В том числе в динамических неймспейсах. Кстати, очень быстро привыкаешь.

>кстати, догадался, про какую "животную ассоциацию" я писал :) ?)
еще б я играл в ассоциации. делать мне больше нечего. опыт у разных людей разный, и ассоциации разные.
а ты тогда угадай, какого цвета мой сотовый телефон.

>Я тут уже где-то писал, что цикл - это statement, а list comprehension - это expression, что подразумевает (хотя вовсе не обязывает, к сожалению) отсутствие сайд-эффектов в list comprehension.
Ну вот видишь, кто-то тебя к чему-то обязывает...
Попробуй поиграть в наивность и посчитать циклы и list comprehensions как аналогичные C-шным циклам ;)

(Ответить)(Уровень выше)


(Добавить комментарий)

Привет, [info]anonymous!
Стань платным пользователем