2009-12-14 2 views
22

«Is 'eval' supposed to be nasty?» вдохновил этот:Когда оправдывается `eval` в Ruby?

В основном все согласны с тем, что eval плохо, и в большинстве случаев есть более элегантный/безопаснее замены.

Итак, я хотел спросить: неправильно ли используется eval, что это действительно необходимо в качестве языковой функции? Это делает больше зла, чем пользы?

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

Редактировать: Цель этого вопроса - получить как можно больше реальных случаев, если eval является единственным или лучшим решением. Поэтому, пожалуйста, не заходите в «если язык ограничивает творчество программиста».

Edit2: И когда я говорю eval, конечно, я имею в виду eval ИНГ строку, не передавая рубиновый блок instance_eval или class_eval.

ответ

20

Единственный случай, который я знаю (кроме «У меня есть эта строка, и я хочу ее выполнить»), динамически обрабатывает локальные и глобальные переменные. Ruby имеет методы для получения имен локальных и глобальных переменных, но ему не хватает методов для получения или установки их значений на основе этих имен. Единственный способ сделать AFAIK - с eval.

Любое другое использование почти наверняка неверно.Я не гуру и не могу категорически заявить, что других нет, но каждый другой случай использования, который я когда-либо видел, где кто-то сказал: «Для этого вам нужен eval», я нашел решение, которое этого не делало.

Обратите внимание, что я говорю о строке eval здесь, между прочим. Ruby также имеет instance_eval, который может принимать либо строку, либо блок для выполнения в контексте приемника. Блочная форма этого метода является быстрой, безопасной и очень полезной.

+2

Я знаю, что это выходит за рамки основного вопроса, но мне любопытно, в каких случаях реального использования вам нужно динамически назначать локальные/глобальные переменные, т. Е. При использовании атрибутов экземпляра и соответствующих методов instance_variable_set и instance_variable_get не работают ? –

+0

Ну, вы не всегда хотите хранить вещи, находящиеся за вызовом метода, и иногда вы хотите использовать локальные переменные, чтобы определить, для чего устанавливаются ивары. Одним из мест, где это может быть полезно, является устранение шаблона. Часто инициализатор принимает аргументы и назначает их одинаковым именованным переменным экземпляра. С помощью 'eval' вы можете сократить это до типа' set_ivars (binding) '. – Chuck

+0

эта идиома полезна и возможна только с eval: 'eval ('self', block.binding)' – horseyguy

7

Причина, по которой eval существует, потому что, когда вам это нужно, когда вам это действительно нужно, нет никаких заменителей. В конце концов, вы можете только так много сделать с помощью диспетчеризации творческих методов, и в какой-то момент вам нужно выполнить произвольный код.

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

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

Когда это оправдано? В прагматичных терминах, когда вы это говорите. Если это ваша программа, и вы программист, вы задаете параметры.

+3

Единственное, что я могу добавить: «Это оправдано, когда вы говорите, что это так, и вы осознаете последствия». –

+0

Мое намерение состояло в том, чтобы как можно больше оправдать использование eval, а не спорить о том, правильно ли сделали автор (-ы) Ruby. Но если вы хотите пойти туда, я бы сказал, что часто я не единственный, кто задает параметры, как вы говорите, как часто я не единственный программист в проекте. И eval - одна из самых опасных особенностей языка, попавшая в чужие руки. –

+1

-1. Обобщения и возвращение мяча в суд Младдена никому не помогают. – z5h

2

eval - инструмент, он не является ни по своей природе хорошим, ни злым. Это оправдано, когда вы уверены, что это правильный инструмент для того, что вы пытаетесь выполнить.

+0

Я в основном согласен. Проблема в том, что те, кто злоупотребляет эха-молотом, начинают ломать вещи только потому, что они не знают языка; например, они переписывают RTL или что-то в этом роде. –

+0

@ The Wicked Flea: вот почему я включил «всякий раз, когда вы уверены».Если вы считаете, что это правильный инструмент, это не так. Только тогда, когда вы знаете, что это правильный инструмент, и знаете последствия этого решения, это действительно правильный инструмент. –

+0

Ну, будучи уверенным, вы автоматически не оправдываете свои решения. Кроме того, я бы сказал, что некоторые инструменты используются для доброкачественной цели чаще других (на ум приходит вспашка и бомба). Хех, политический комментарий за политический ответ. :( –

1

Инструмент, подобный eval, посвящен оценке кода во время выполнения и «компиляции». Вы знаете, что такое код при запуске Ruby? Тогда вам, вероятно, не понадобится eval. Является ли код генерации кода во время выполнения? то вам, вероятно, нужно это оценить.

Например, методы/функции, необходимые для рекурсивного приличного парсера, зависят от языка, который обрабатывается. Если ваше приложение создает такой синтаксический анализатор «на лету», тогда имеет смысл использовать eval. Вы можете написать обобщенный синтаксический анализатор, но это может быть не столь элегантное решение.

«Programatically filling in a letrec in Scheme. Macros or eval?» - это вопрос, который я опубликовал об Эвале на Схеме, где его использование в основном неизбежно.

+2

Вы можете создавать функции на runtime без использования eval – Chuck

+0

Напишите код, который генерирует набор взаимно-рекурсивных функций без eval. Затем с eval. Моим утверждением является то, что второе упражнение обычно будет проще. – z5h

+0

В Ruby способ генерации методов с и без 'eval' почти то же самое, поэтому я не уверен, почему это было бы иначе? – henrikhodne

12

Когда это оправдано? Я бы сказал, когда нет разумной альтернативы. Я мог думать об одном использовании, где я не могу придумать альтернативу: irb, который, если вы копаете достаточно глубоко (до workspace.rb, вокруг строки 80 в моей копии, если вам интересно) использует eval для выполнения вашего ввода:

def evaluate(context, statements, file = __FILE__, line = __LINE__) 
    eval(statements, @binding, file, line) 
end 

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

5

Существует один очень важный прецедент для eval(), который не может быть достигнут (AFAIK), используя что-либо еще, и это должно найти соответствующую ссылку на объект для привязки.

Предположим, вы прошли блок, но (по некоторым причинам) вам необходимо получить доступ к объекту контекст привязки, вы могли бы сделать следующее:

obj = eval('self', block.binding) 

Это также полезно, чтобы определить следующее:

class Proc 
    def __context__ 
     eval('self', self.binding) 
    end 
end 
1

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

Следует отметить, что в рубине instance_eval и class_eval имеют другие применения.

Смежные вопросы