2009-12-14 1 views
4

У меня есть строка (аутентифицированная, надежная и т. Д.), Содержащая исходный код, предназначенный для работы в цикле Ruby, быстро. В Python, я бы компилировать строку в абстрактное синтаксическое дерево и eval() или exec() его позже:Компилировать строку для байт-кода Ruby для лучшей производительности - например, compile() в Python

# Python 3 example 
given_code = 'n % 2 == 1' 
pred = compile(given_code, '<given>', 'eval') 
print("Passed:", [n for n in range(10) if eval(pred)])  
# Outputs: Passing members: [1, 3, 5, 7, 9] 

Рубин не имеет функцию компиляции, так что это лучший способ для достижения этой цели?

ответ

7

На основе решения JHS, но непосредственно с помощью лямбда в качестве тела цикла (The & вызывает to_proc на лямбда и передает его в качестве блока к функции выбора).

given_code = 'n % 2 == 1' 
pred = eval "lambda { |n| #{given_code} }" 
p all = (1..10).select(&pred) 
+1

Это чище, чем я это делал. Благодаря! Я бы добавил, что это выходит за рамки этого вопроса: создатель 'given_code' может захотеть взять на себя ответственность за список формальных параметров. Например, он должен поставить '| n | n% 2 == 1' или даже '{| n | n% 2 == 1} '. – JasonSmith

+0

Ruby терпит неудачу, если количество переданных аргументов не соответствует количеству ожидаемых аргументов. Таким образом, вы можете проверить строку, когда получите, если от создателя. – akuhn

+0

Хорошая точка. В моем конкретном случае администратор (возможно, я в основном) устанавливает предикат для того, интересна ли запись в журнале; поэтому я могу документировать соглашение о параметрах. Главное, чтобы он был достаточно быстрым, поскольку он обрабатывает журналы. Я не готов к отправке на C или что-то еще, кроме любых плохих фруктов, которые я могу получить в Ruby, я возьму. – JasonSmith

1

Я обертываю всю строку в лямбда (еще как строку), eval, а затем вызываю результирующий объект Proc.

# XXX: Only runs on Ruby 1.8.7 and up. 
given_code = 'n % 2 == 1' 
pred = eval "lambda { |n| #{given_code} }" 
puts 1.upto(10).select { |x| pred.call(x) } .inspect # Or (1..10).select for Ruby <= 1.8.6 
# Output: [1, 3, 5, 7, 9] 
+0

Просто интересно, если у других есть идея лучше или если это предпочтительная идиома. – JasonSmith

+0

не компилируется, 'Integer # upto' ожидает блок (он не возвращает перечислимый) – akuhn

+0

@Adrian Я подтвердил этот код на Ruby 1.8 и 1.9 на Linux. Просьба уточнить и/или пересмотреть нижний план. Благодарю. – JasonSmith