LINUX.ORG.RU

Впечатления рубиста от django


1

6

Долгое время писал под RoR и вот появилась работа под Django. Я поражён, это так неудобно.

1. По каждому чиху нужно писать импорты. На кой спрашивается? Это фреймвёрк для облегчения разработки или что?

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

3. Нет наследования всех контроллёров от одного (ApplicationController), таким образом совершенно не понятно как создать переменные доступные во всех темплейтах.


Ответ на: комментарий от geekless

Вопрос: ссылка не позволяет присваивая в себя значение, заменять объект, на который она ссылается?

Вообще-то, в PHP давно все объекты по умолчанию итак передаются по ссылке. Но ты, наверное, имеешь в виду скалярные переменные? Да, они меняются, если ссылку на них присвоить переменной. Всё как в том же Си++. Но, во-первых, у тебя в коде этого нет, во-вторых, у тебя нет сторонних переменных, которые ты мучаешь по ссылке.

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

А результат — в $result

Тогда это каша какая-то. (Не вдаваясь в подробности неверного синтаксиса).

А результат — в $result. Двумерная матрица. По идее.

Тогда тебе не нужны никакие ссылки.

$matrix = array();
foreach(file("file") as $line)
{
    $row = array();
    foreach(explode(" ", chop($line)) as $col)
       $row[] = to_complex($col);
    $matrix[] = $row;
}

Вопросы инициализации можно выкинуть, но тогда это будет шаг на пути к W/O. Можно также =array(); поменять на =[]; но это ещё не на всех хостингах есть. В 5.4 ввели.

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

Вообще-то, в PHP давно все объекты по умолчанию итак передаются по ссылке. Но ты, наверное, имеешь в виду скалярные переменные? Да, они меняются, если ссылку на них присвоить переменной. Всё как в том же Си++. Но, во-первых, у тебя в коде этого нет, во-вторых, у тебя нет сторонних переменных, которые ты мучаешь по ссылке.

Тогда это каша какая-то. (Не вдаваясь в подробности неверного синтаксиса).

In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference.

$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
    $value = $value * 2;
}

Официальный ман на официальном сайте. С официальными примерами.

Эээ... Расскажешь им про неверный синтаксис?

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

А, да, конечно же ещё так:

map to_complex потерялся в коде.

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

Официальный ман на официальном сайте. С официальными примерами.

Ок. Принимается. Разобрался. Видишь ли, ты даже на PHP пытаешься писать write-only код :) Чисто для того, чтобы посложнее было, что ли? Чем мой последний вариант не нравится? :)

Если с функцией (в последний раз забыл, да), то так будет:

$result = array();
foreach(file("file.dat") as $line)
    $result[] = array_map('to_complex', explode(" ", chop($line)));

Никаких подводных камней со ссылками, без лишних переменных и короче.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73
$matrix = array();
foreach(file("file") as $line)
{
    $row = array();
    foreach(explode(" ", chop($line)) as $col)
       $row[] = to_complex($col);
    $matrix[] = $row;
}

Вопросы инициализации можно выкинуть, но тогда это будет шаг на пути к W/O.

На мой взгляд, это и есть самый настоящий W/O. За всеми этими $row[]= и $matrix[] = абсолютно исчезает непосредственное предназначение кода. Если в коротенький код приходится всматриваться, чтобы понять, для чего он нужен, это фейл читабельности. В этот — приходится.

Еще добавлю, что отдельного фейла в твоих примерах заслуживает использование file() с последующим chop(). Полностью в духе консольных утилит, каждая из которых решает половину своей и половину чужой задачи, и вот соединяя их вместе в конвейер, мы пытаемся как-то при помощи костылей решить обе.

Вариант с явным вызовом file_get_contents() и explode() хоть и выглядит длиннее, зато более понятен.

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

Видишь ли, ты даже на PHP пытаешься писать write-only код :)

Вариант с циклом мой более понятен, чем твой с []=. Я настаиваю. :-D

Варианты array_map любые читабельны только если вызывать как array_map('имя_функции', ...). Если подставлять лямбду, получается каша.

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

На мой взгляд, это и есть самый настоящий W/O

Во-первых: Впечатления рубиста от django (комментарий)

Во-вторых, он линеен и более последователен в отличие от типичных ходов в Ruby.

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

Вариант с явным вызовом file_get_contents() и explode() хоть и выглядит длиннее, зато более понятен.

Совершенно идентичные решения. В моём у нас прямая работа со строками файла, что логично. А сохранение перевода строк (привет, Perl!) приходится решать откусыванием. Дополнительный бонус — более экономная работа с памятью, файл не грузится целиком. Твой вариант (на самом деле я сам предпочитаю такой использовать, но это дело привычки, а не логики) рассматривает файл как монолитный блок, вводя сущность в виде переводов строк. Шило на мыло. Так что я выбрал вариант с file() как более короткий. Хотя сам на автомате, скорее, писал бы вариант с file_get_contents().

В любом случае, хотя ты и потянут на меряние объёмом кода, на практике он не актуален, поскольку средний программист итак в день пишет строк 8 кода. Куда важнее, чтобы год спустя ты код читал не разбираясь в нём, сразу видел бы, что к чему. Вот тут и Ruby не фонтан (наследие Perl), и твой вариант с PHP — тоже.

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

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

Вариант с циклом мой более понятен, чем твой с []=. Я настаиваю. :-D

Я понимаю, что после Ruby тебя тянет извращаться с закорючками ссылок :)

Если подставлять лямбду, получается каша

Хорошо ещё что ты, видимо, JS не видел :D Вот образец языка куда более W/O, чем Ruby :)

Но лямбды в любом виде снижают читаемость. Лучше вынести в отдельную функцию даже если она вызывается один раз. Заодно грамотное название функции служит самодокументированию. И тестировать проще, и повторное использование, как понадобится — «бесплатное».

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

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

В коде File.read('in').lines{|l|l.split.map(&:to_c)} мозг сразу выделяет два сегмента: File.read('in').lines и l.split.map(&:to_c). И я сразу вижу, что этот код делает. Кстати, я ошибся в изначальном примере, правильнее так: File.read('in').split(«\n»).map{|l|l.split.map(&:to_c)}

А все эти упоительные истории про «лепить серию операций в одну строку — это очень сильно снижает читабельность кода независимо от языка и синтаксиса» — это всё конечно интересно, но не в тему. Этот код можно записать в одну строку, или две, ничего не изменится — в нём всё равно сразу видно два шаблона кода, как ты его ни пиши.

Про «год спустя», кстати, тоже мимо, т.к. в таком примерно режиме я пишу. Я на самом деле именно на Ruby не так уж часто программирую. И код, который я читаю, обычно для меня чужой, я давно уже забыл, что тут и как. И всё прекрасно понятно, никакого W/O.

А читать якобы трудно, потому что мозги привыкли к многострочным шаблонам — искать границы циклов и условия выхода из них, переменные-счётчики, переменные для накопления результата и т.п. А тут этого ничего практически никогда нет.

Но специально для любителей:

matrix = []
lines = File.read('in').split("\n")
lines.each{|line|
    matrix << line.split.map(&:to_c)
}

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

Хорошо ещё что ты, видимо, JS не видел :D

В JS с подстановкой лямбд в параметры всё гораздо лучше, чем в пхп. Там вовремя разупоролись и сделали нормальные array.map() и т.п.

Но сам синтаксис лямбд такой же угребищный. В какой-нибудь function(v) {return bla(v) + foo(v)} больше половины символов занимают function-return — мусор, который надо давно выкинуть из синтаксиса.

Но лямбды в любом виде снижают читаемость. Лучше вынести в отдельную функцию даже если она вызывается один раз.

facepalm()

Заодно грамотное название функции служит самодокументированию.

facepalm(double)

и повторное использование, как понадобится — «бесплатное».

facepalm(triple)

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

geekless ★★
()

Тред восстал из мертвых усилиями троллей-неосиляторов питона?

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

Нет, всё ж прокомментирую.

грамотное название функции служит самодокументированию

Если у меня в лямбде так и написано: l.split.map(&:to_i) — «разделить на элементы и сконвертировать в числа». Или вот так написано: data.transpose.map{|col| col.map(&:size).max} — «транспонировать и взять максимальный размер по строкам». За каким чертом нужны какие-то «самодокументирующие» названия? :)

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

За каким чертом нужны какие-то «самодокументирующие» названия? :)

Ладно, проехали. Вернёмся к этой теме лет через 10 твоей практики :)

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

Опять ты с PHP сравниваешь :)

Не я, а ты. Код, на котором ты вспомнил JS, был на пхп.

Ладно, проехали. Вернёмся к этой теме лет через 10 твоей практики :)

Надеюсь, ты за 10 лет найдешь возможность ознакомиться с критериями, по которым определяется, какой код в отдельную именованную функцию выносить имеет смысл, а какой — нет. :}

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

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

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

Видимо, куда-то не туда ты пришел, если считаешь нужным выносить код

А я выше указал почему.

Вообще, Thinking Forth. Это одно из первых правил :) Декомпозиция рулит.

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

«не обладающий значимой семантикой вне рамок того места, откуда он вызывается»

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

(пожимая плечами) Ну, вот тебе Python с гарниром из лямбда-каши:

maxlen = max([max(map(len, row)) for row in matr])
rj, lj = lambda s: s.rjust(maxlen), lambda s: s.ljust(maxlen)
aligned_rows = [" | ".join([lj(elt) if n < 2 else rj(elt) 
                            for (n, elt) in enumerate(row)]) 
                for row in matr]
print "\n".join(["| " + r + " |" for r in aligned_rows])

Немного длиннее, чем на Ruby, но о 2-кратном проигрыше речь не идет.

Кстати, прочитал ман на лямбды пхп, и вот как оно получается:

Получается, что Ruby не выигрывает в 2 раза и у PHP.

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

с гарниром из лямбда-каши

каши

Ну ты понял, да?

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

каши

Ну ты понял, да?

Да. Ты хочешь сказать, что каша это только на Питоне, а на Руби - кристально чистый и прозрачный код, к тому же в 5 раз короче.

Кстати, этот алгоритм другую задачу решает.

geekless> Не надо обезьяннить мой алгоритм один-в-один.

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

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

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

Да. Ты хочешь сказать, что каша это только на Питоне, а на Руби - кристально чистый и прозрачный код,

Именно это я и хочу сказать.

geekless ★★
()
Ответ на: комментарий от geekless
col_size =  [max([len(x) for x in xs]) for xs in zip(*matr)]
for n, row in enumerate(matr):
    print "|{}|".format("|".join([ 
            el.ljust(col_size[k]) if k<2 else el.rjust(col_size[k]) 
            for (k, el) in enumerate(row)]))
zz ★★★★
()
Ответ на: комментарий от zz

zip из коробки не может в списки разной длинны, поэтому ущербный if c k<2 еще с нами, надо чтото придумать с ним :)

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

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

maxlen = [max(map(len, col)) for col in zip(*matr)]
rj, lj = lambda s, i: s.rjust(maxlen[i]), lambda s, i: s.ljust(maxlen[i])
aligned_rows = [[lj(elt, n) if n < 2 else rj(elt, n)
                 for (n, elt) in enumerate(row)]
                for row in matr]
print "\n".join(["| " + " | ".join(r) + " |" for r in aligned_rows])

(join прямо в list comprehension мне никогда не нравился %)

Ты хочешь сказать, что каша это только на Питоне, а на Руби - кристально чистый и прозрачный код,

Именно это я и хочу сказать.

Никто и не ожидал другого. Хорошо хоть, что утверждение про 2-5 раз больше не озвучивается.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Хорошо хоть, что утверждение про 2-5 раз больше не озвучивается.

А зачем его озвучивать, если и так видно что код примерно раза в 2 больше получился. :}

geekless ★★
()
Ответ на: комментарий от zz
col_size  = [max(map(len, col)) for col in zip(*matr)]
for row in matr:
    print "|%s|" % "|".join([ 
            (value.rjust, value.ljust)[align_left==True](size)
            for (value, size, align_left) in map(None, row, col_size, [True, True])])

И у нас есть победитель!

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

и так видно что код примерно раза в 2 больше получился. :}

Доо. 297 байт против 206 - это, конечно, именно в 2 раза.

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

[(value.rjust, value.ljust)[align_left==True](size) for (value, size, align_left) in map(None, row, col_size, [True, True])]

Оригинально, но по-прежнему читабельно с трудом. По сути интересное упражнение на тему «как бороться с языком чтобы делать то, для чего он не предназначен».

Чисто ради интереса я переписал свой алгоритм слово в слово на Io.

col_sizes := data transpose map(map(size) max)
data map(
	zip(col_sizes, [true, true]) map([value, size, align_left],
		if (align_left, value ljust(size), value rjust(size))
	) join(" | ")
) join("\n") print

(На самом деле, там из коробки нет transpose и zip, а map нас не поймёт, если вот так передавать список имён: [value, size, align_left]. Но предположим, что у нас Io с блэкджеком и более фичастой стандартной библиотекой.)

И хотя лаконичность повысилась, читабельность значительно упала. Потому что в руби легко вычленяются отдельные фрагменты-шаблоны, в чем сильно помогают те самые фигурные скобки и «пайпы», от которых так страдает KRoN73. А Io, где любой фрагмент кода может означать что угодно, и всё выглядит одинаково, читать намного труднее.

geekless ★★
()
Последнее исправление: geekless (всего исправлений: 1)
Ответ на: комментарий от geekless

>> Доо. 297 байт против 206 - это, конечно, именно в 2 раза.
> 297 против 170 ты хотел сказать.

Что даст подсчет длины кода в _байтах_? Считать необходимо количество логических единиц, на которые по факту задача разбита в коде реализации

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

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

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

Вопрос о читаемости на таком маленьком фрагменьте кстате вполне дискуссионный, но бОльший кусок брать нет смысла, там скорее всего и без лямбдодрочерства на питоне получится более-менее такая же длинна.

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

И хотя лаконичность повысилась, читабельность значительно упала. Потому что в руби легко вычленяются отдельные фрагменты-шаблоны, в чем сильно помогают те самые фигурные скобки и «пайпы», от которых так страдает KRoN73. А Io, где любой фрагмент кода может означать что угодно, и всё выглядит одинаково, читать намного труднее.

И эти люди якобы фейспалмят, когда крон говорит о нечитабельности лиспа... и сами же плюются от однородного (и вполне удобного) синтаксиса в (хоть и абсолютно ненужном) Io. Человек, которому нравится руби-стиль может расхваливать лисп разве что с целью показаться «крутым», мол «я тоже причащаюсь вместе с вами» :D

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

297 байт против 206 - это, конечно, именно в 2 раза.

297 против 170 ты хотел сказать.

Нет, я хотел сказать то, что сказал.

col_size = data.transpose.map{|col| col.map(&:size).max}
puts data.map{|row|
row.zip(col_size, [true, true]).map{|value, size, align_left|
align_left ? value.ljust(size) : value.rjust(size)
}.join(" | ")
}
maxlen = [max(map(len, col)) for col in zip(*matr)]
rj, lj = lambda s, i: s.rjust(maxlen[i]), lambda s, i: s.ljust(maxlen[i])
aligned_rows = [[lj(elt, n) if n < 2 else rj(elt, n)
for (n, elt) in enumerate(row)]
for row in matr]
print "\n".join(["| " + " | ".join(r) + " |" for r in aligned_rows])

Кстати, 297 против 170 тоже не дает 2 раз.

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

Вопрос по сути: как выделить «логические единицы»?

Если пишешь в функциональном стиле - функции из декомпозиции функций, если в декларативном - декларации, если в императивном - операторы, например.

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

Вопрос по сути: как выделить «логические единицы»?

Токенами грамматики языка. Но вангую, что для Руби это будет еще хуже, чем по байтам.

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

> там скорее всего и без лямбдодрочерства
> лямбдодрочерства

В языках с нормальной поддержкой лямбд проблема отсутствует

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

Человек, которому нравится руби-стиль может расхваливать лисп разве что с целью показаться «крутым», мол «я тоже причащаюсь вместе с вами» :D

Ништяки Io и лиспа в гомоиконности, а не в читабельности кода. Где я расхваливал лисп?

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

Читать как: по сравнению с языком с нормальной поддержкой лямбд.

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

Кстати, я ошибся в изначальном примере, правильнее так: File.read('in').split(«\n»).map{|l|l.split.map(&:to_c)}

res = File.open(«in»).readlines.map{ |l| l.split.map(&:to_c)}

Ну и

class String
         def to_c
                 Complex.new(self.to_i, 0)
         end
end

перед этим не забываем, да. ;)

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

как выделить «логические единицы»?

Токенами грамматики языка.

Узлы AST.

K.O.

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

Ништяки Io и лиспа в гомоиконности

Кроме постфиксной нотации (лисп в обратную сторону, но с голимым ооп, ко всему еще и прототипным) и гомоиконности никаких прочих ништяков в Io нет.

а не в читабельности кода

Руби-код по-твоему более читабелен, чем лисп? о_О У нас тут уже есть один лисп с разными скобками, восторгов не вызывает.

Где я расхваливал лисп?

KRoN73> И, наоборот, чистый, понятный и строгий язык может не быть популярным.
geekless> Ага. Например, CL.

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