Какие функции динамического программирования в Perl мне следует использовать? - PullRequest
11 голосов
/ 02 сентября 2010

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

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

Все это нормально, но какие специфические особенности я должен иметь, имея некоторый опыт в C и C ++, при написании кода на Perl, чтобы использовать все имеющиеся у него функции динамического программирования для создания какого-то потрясающего кода?

Ответы [ 9 ]

15 голосов
/ 02 сентября 2010

Этого вопроса более чем достаточно, чтобы заполнить книгу.Фактически, именно это и произошло!

Отличный Perl высшего порядка Марка Джейсона Доминуса доступен онлайн бесплатно.

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

Примерно в 1993 году я начал читать книги о Лиспе и обнаружил кое-что важное: Perl гораздо больше похож на Лисп, чем на С. Если выВозьмите хорошую книгу о Лиспе, там будет раздел, в котором описываются его хорошие возможности.Например, книга «Парадигмы программирования искусственного интеллекта» Питера Норвига включает раздел «Что отличает Лисп?»это описывает семь особенностей Lisp.Perl разделяет шесть из этих функций;С не разделяет ни один из них.Это большие важные функции, такие как первоклассные функции, динамический доступ к таблице символов и автоматическое управление хранилищем.

10 голосов
/ 02 сентября 2010

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

Ваша цель не должна заключаться в том, чтобы найти проблемы для ваших решений. Изучите немного больше Perl, чем вы планируете использовать немедленно (и продолжайте учиться). Однажды вы столкнетесь с проблемой и подумаете: «Я помню кое-что, что могло бы помочь с этим».

Возможно, вы захотите посмотреть некоторые из этих книг:

  • Perl высшего порядка
  • Мастеринг Perl
  • Эффективное программирование на Perl

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

10 голосов
/ 02 сентября 2010

Список привычек C, которые нельзя перенести в Perl 5:

  • Не объявляйте свои переменные в начале программы / функции. Объявите их, как они нужны.
  • Не назначайте пустые списки массивам и хэшам при их объявлении (они уже пусты и их нужно инициализировать).
  • Не используйте if (!(complex logical statement)) {}, для этого unless.
  • Не используйте goto для разбиения глубоко вложенных циклов, next, last и redo все принимают метку цикла в качестве аргумента.
  • Не используйте глобальные переменные (это общее правило даже для C, но я обнаружил, что многие люди из C любят использовать глобальные переменные).
  • Не создавайте функцию, для которой будет выполняться замыкание (в частности, обратные вызовы). См. perldoc perlsub и perldoc perlref для получения дополнительной информации.
  • Не используйте возвраты in / out, вместо этого возвращайте несколько значений.

Что нужно сделать в Perl 5:

  • Всегда используйте прагмы strict и warnings.
  • Прочитайте документацию (perldoc perl и perldoc -f function_name).
  • Используйте хэши так, как вы использовали structs в C.
6 голосов
/ 02 сентября 2010

Я думаю, что самым большим препятствием будет не динамический аспект, а аспект «батареи включены».

Я думаю, что наиболее мощными аспектами Perl являются

  • хешей: они позволяют легко выразить очень эффективные структуры данных
  • регулярные выражения: они действительно хорошо интегрированы.
  • использование переменных по умолчанию, таких как $ _
  • библиотеки и CPAN для чего не установлено стандарт

Что-то, что я заметил в C-преобразованиях, это чрезмерное использование циклов for. Многие могут быть удалены с помощью grep и map

Еще один девиз perl: «Есть несколько способов сделать это». Чтобы подняться по кривой обучения, вы должны часто говорить себе: «Должен быть лучший способ сделать это, я не могу быть первым, кто хочет сделать это ...». Тогда вы, как правило, можете обратиться к Google и CPAN с невероятным количеством библиотек.

Кривая изучения Perl не крутая, но очень длинная ... не торопитесь и наслаждайтесь поездкой.

3 голосов
/ 02 сентября 2010

Два очка.

Во-первых, в общем, я думаю, что вам следует задать себе два слегка отличающихся вопроса:

1) Какие функции динамического программирования Perl можно использовать в каких ситуациях /решить какие проблемы?

2) Каковы компромиссы, подводные камни и недостатки каждой функции.

Тогда ответ на ваш вопрос становится предельно очевидным: вы должны использовать функции, которыерешить вашу проблему лучше (с точки зрения производительности или удобства сопровождения кода), чем сопоставимое решение без DP, и которое требует меньше, чем максимально допустимый уровень недостатков.

В качестве примера можно привести цитату из комментария FM, строковую формуeval имеет некоторые довольно неприятные недостатки;но в некоторых случаях это МОЖЕТ быть чрезвычайно элегантным решением, которое на несколько порядков лучше, чем любой альтернативный подход DP или SP.


Во-вторых, имейте в виду, что в Perl есть много функций "динамического программирования"на самом деле упакованы для вас в чрезвычайно полезные модули, которые вы можете даже не распознать как имеющие характер DP.

Мне придется подумать о наборе хороших примеров, но тот, который сразу приходит на ум, - это текст.шаблонные модули, многие из которых реализованы с использованием вышеупомянутой строковой формы eval;или Try :: Tiny механизм исключения, который использует блочную форму eval.

Еще один пример - аспектное программирование, которое может быть достигнуто с помощью Moose (сейчас я не могу найти соответствующую ссылку на StackOverflow - если кто-то имеетпожалуйста, отредактируйте в ссылке) - под которой используется доступ к таблице символов с изображением DP.

2 голосов
/ 02 сентября 2010

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

Я проходил обучение в C и C ++ в колледже и позже изучил Perl. Perl великолепен для быстрых решений и некоторых действительно долговечных решений. Я построил компанию на Perl и Oracle для решения логистических задач для DoD с около 100 активными программистами. У меня также есть некоторый опыт в управлении привычками других программистов Perl, новыми и старыми. (Я был основателем / генеральным директором, а не непосредственно занимался техническим менеджментом ...)

Я могу только прокомментировать мой переход на Perl-программист и то, что я видел в своей компании. Многие из наших инженеров поделились своим опытом в первую очередь быть программистами на C / C ++ по выбору и программистами на Perl.

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

Мы наняли (и уволили) многих программистов за 5 лет, что у меня была компания. Распространенным вопросом интервью было следующее: Напишите короткую программу на Perl, которая будет печатать все нечетные числа от 1 до 50 включительно, разделенные пробелом между каждым числом и оканчивающиеся символом CR. Не используйте комментарии. Они могут сделать это в свое собственное время на несколько минут и могут сделать это на компьютере, чтобы проверить вывод.

После того, как они написали сценарий и объяснили его, мы попросили бы его изменить его, чтобы он печатал только четные данные (перед интервьюером), а затем получили шаблон результатов, основанный на каждой четной цифре, каждой нечетной, кроме каждого седьмого и 11-го в качестве примера. Другим потенциальным модом будет каждый четный в этом диапазоне, нечетный в этом диапазоне, без простых чисел и т. Д. Цель состояла в том, чтобы проверить, выдержал ли их первоначальный небольшой сценарий изменение, отладку и обсуждение другими пользователями, и думали ли они заранее, что спецификация может измениться.

Хотя в тесте не говорилось «в одну строку», многие взяли на себя задачу сделать его одной краткой строкой и с ценой читабельности. Другие сделали полный модуль, который просто занял слишком много времени, учитывая простую спецификацию. Нашей компании нужно было очень быстро обработать твердый код; вот почему мы использовали Perl. Нам нужны были программисты, которые думали так же.

Все представленные ниже фрагменты кода делают одно и то же:

1) Слишком похоже на C, но его очень легко изменить. Из-за цикла аргумента for в стиле C 3 требуется больше подверженных ошибкам модификаций, чтобы получить альтернативные циклы. Простая отладка и обычное представление. Любой программист практически на любом языке поймет это. Ничего особенно плохого в этом нет, но не убийца:

for($i=1; $i<=50; $i+=2) {
    printf("%d ", $i);
} 
print "\n";

2) Очень похоже на Perl, легко получить четность, легко (с подпрограммой) получить другие циклы или шаблоны, легко понять:

print  join(' ',(grep { $_ % 2 } (1..50))), "\n"; #original
print  join(' ',(grep { !($_ % 2) } (1..50))), "\n"; #even
print  join(' ',(grep { suba($_) } (1..50))), "\n"; #other pattern

3) Слишком идиоматичный, немного странный, почему он пробел между результатами? Собеседник допустил ошибку в получении четного Сложнее отлаживать или читать:

print "@{[grep{$_%2}(1..50)]}\n";   #original
print "@{[grep{$_%2+1}(1..50)]}\n"; #even - WRONG!!!
print "@{[grep{~$_%2}(1..50)]}\n"; #second try for even

4) Умно! Но тоже слишком идиоматичен. Нужно подумать о том, что происходит с хешем annon, созданным из списка операторов диапазона, и почему это создает шансы и четности. Невозможно изменить другой шаблон:

print "$_ " for (sort {$a<=>$b} keys %{{1..50}}), "\n"; #orig
print "$_ " for (sort {$a<=>$b} keys %{{2..50}}), "\n"; #even
print "$_ " for (sort {$a<=>$b} values %{{1..50}}), "\n"; #even alt

5) Kinda C, как снова, но прочная основа. Легко изменить за пределы четного / нечетного. Очень читабельно:

for (1..50) { 
    print "$_ " if ($_%2); 
    }              #odd
print "\n";

for (1..50) { 
    print "$_ " unless ($_%2); 
    } #even
print "\n";

6) Возможно, мой любимый ответ.Очень Perl, как и все же читаемый (для меня в любом случае) и пошаговый в формировании и справа налево в потоке.Список находится справа и может быть изменен, обработка сразу же слева, повторное форматирование слева, последняя операция «печать» слева.

print map { "$_ " } grep { $_ & 1 } 1..50;  #original
print "\n";
print map { "$_ " } grep { !($_ & 1) } 1..50;  #even
print "\n";
print map { "$_ " } grep { suba($_) } 1..50;  #other
print "\n";

7) Это мой наименее любимый заслуживающий доверия ответ.Ни C, ни Perl невозможно изменить без потрошения цикла, в основном показывая, что заявитель знал синтаксис массива Perl.Он очень хотел получить заявление по делу ...

for (1..50) { 
    if ($_ & 1) { 
        $odd[++$#odd]="$_ ";
        next;
    } else {    
        push @even, "$_ ";
    }
}   
print @odd, "\n";
print @even;

Опрошенные с ответами 5, 6, 2 и 1 получили работу и справились хорошо.Ответы 7,3,4 не получили наем.

Ваш вопрос касался использования динамических конструкций, таких как eval или других, которые вы не можете сделать на чисто скомпилированном языке, таких как C. Этот последний пример является "динамическим" с eval в регулярном выражении, но действительно плохим стилем:

$t='D ' x 25;
$i=-1;
$t=~s/D/$i+=2/eg;
print "$t\n";     # don't let the door hit you on the way out...

Многие скажут вам "не пишите C на Perl".Я думаю, что это только частично правда.Ошибка и ошибка в том, что нужно жестко писать новый код Perl в стиле C, даже если в Perl так много выразительных форм.Используйте те.И да, не пишите НОВЫЙ код Perl в стиле C, потому что синтаксис C и идиома - это все, что вы знаете.(плохая собака - без печенья)

Не пишите динамический код на Perl только потому, что вы можете.Есть определенные алгоритмы, которые вы встретите, и вы скажете: «Я не совсем знаю, как написать TH на C», и многие из них используют eval.Вы можете написать регулярное выражение Perl для анализа многих вещей (XML, HTML и т. Д.), Используя рекурсию или eval в регулярном выражении, но вы не должны этого делать.Используйте синтаксический анализатор точно так же, как в C. Есть определенные алгоритмы, хотя eval - это подарок.Исправление имени файла Ларри Уолла переименование потребовало бы намного больше кода C для репликации, не так ли?Есть много других примеров.

Также не избегайте строгого определения стиля C.Форма аргумента C 3 цикла for может идеально подходить для определенных алгоритмов.Кроме того, помните, почему вы используете Perl: предположительно для высокой производительности программиста.Если у меня есть полностью отлаженный кусок кода C, который делает именно то, что я хочу, и мне это нужно в Perl, я просто переписываю глупый стиль C в Perl!Это одна из сильных сторон языка (но также и его слабость для более крупных или командных проектов, где отдельные стили кодирования могут различаться и затруднять соблюдение общего кода.)

Безоговорочно устная реакция на это интервьюВопрос (от заявителя, который написал ответ 6) был: Эта единственная строка кода соответствует спецификации и может быть легко изменена.Однако есть много других способов написать это.Правильный путь зависит от стиля окружающего кода, от того, как он будет вызываться, от соображений производительности и от того, может ли измениться формат вывода. Чувак!Когда ты можешь начать??(Он оказался в управлении BTW.)

Я думаю, что отношение также относится к вашему вопросу.

1 голос
/ 02 сентября 2010

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

1 голос
/ 02 сентября 2010

По крайней мере, IME, «динамическая» природа не так уж важна. Я думаю, что самое большое различие, которое вы должны принять во внимание, состоит в том, что в C или C ++ вы в основном привыкли к тому, что использование библиотечного кода имеет лишь довольно незначительное преимущество. То, что находится в библиотеке, уже написано и отлажено, так что это удобно, но если толчок наступит, вы, как правило, можете сделать почти то же самое самостоятельно. Для большей эффективности вопрос заключается в том, перевешивает ли ваша способность писать что-то более специализированное способность автора библиотеки тратить больше времени на полировку каждой процедуры. Однако есть небольшая разница: если библиотечная процедура действительно не выполняет то, что вы хотите, вам лучше написать свою собственную.

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

0 голосов
/ 02 сентября 2010

Есть много вещей, которые вы можете делать только с динамическим языком, но самый крутой - eval. Подробнее см. здесь .

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

Например,

$Double = "print (\$Ord1 * 2);";

$Opd1 = 8;
eval $Double;  # Prints 8*2 =>16.

$Opd1 = 7;
eval $Double;  # Prints 7*2 =>14.

Переменная $Double является строкой, но мы можем выполнить ее как обычный оператор. Это не может быть сделано в C / C ++.

Круто то, что строкой можно манипулировать во время выполнения; следовательно, мы можем создать команду во время выполнения.

# string concatenation of operand and operator is done before eval (calculate) and then print.
$Cmd = "print (eval (\"(\$Ord1 \".\$Opr.\" \$Ord2)\"));";

$Opr  = "*";
$Ord1 = "5";
$Ord1 = "2";

eval $Cmd;  # Prints 5*2 => 10.

$Ord1 = 3;
eval $Cmd;  # Prints 5*3 => 15.

$Opr = "+";
eval $Cmd;  # Prints 5+3 => 8.

eval очень мощный, так что (как у Человека-паука) власть приходит с ответственностью. Используйте это с умом.

Надеюсь, это поможет.

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