2013-07-02 3 views
6

Учитывая некоторые local variables, что было бы самым простым способом для compact их в Ruby?Что такое Ruby-эквивалент компакт-диска PHP?

def foo 
    name = 'David' 
    age = 25 
    role = :director 
    ... 
    # How would you build this: 
    # { :name => 'David', :age => 25, :role => :director } 
    # or 
    # { 'name' => 'David', 'age' => 25, 'role' => :director } 
end 

В PHP, я могу просто сделать это:

$foo = compact('name', 'age', 'role'); 
+2

http://stackoverflow.com/questions/6589894/access-local-variables-from-a-different-binding-in-ruby –

+3

Я честно озадачен тем, что PHP допускает такие aberra ..., mmm, неортодоксальный код , Действительно ли 'compact' действительно принимает локальные переменные по имени и получает доступ к ним из другой функции? – tokland

ответ

9

я придумал значительное улучшение на мой первоначальный ответ. Это чище, если вы наследуете от Binding. to_sym есть, потому что более старые версии ruby ​​имеют local_variables как строки.

метод экземпляра

class Binding 
    def compact(*args) 
    compacted = {} 
    locals = eval("local_variables").map(&:to_sym) 
    args.each do |arg| 
     if locals.include? arg.to_sym 
     compacted[arg.to_sym] = eval(arg.to_s) 
     end 
    end 
    return compacted 
    end 
end 

использование

foo = "bar" 
bar = "foo" 
binding.compact("foo") # => {:foo=>"bar"} 
binding.compact(:bar) # => {:bar=>"foo"} 

Оригинальный ответ

Это неа остальное я мог бы получить к способу, который ведет себя как compact РНР -

метод

def compact(*args, &prok) 
    compacted = {} 
    args.each do |arg| 
    if prok.binding.send(:eval, "local_variables").include? arg 
     compacted[arg.to_sym] = prok.binding.send(:eval, arg) 
    end 
    end 
    return compacted 
end 

пример использования

foo = "bar" 
compact("foo"){} 
# or 
compact("foo", &proc{}) 

Его не идеально, хотя, потому что вы должны пройти процедурный Я открыт для предложений о том, как улучшить это.

+1

Кажется, что вы можете разместить 'eval' вне цикла, нет? –

+0

@ Сергио Туленцев благодарит за это. – Bungus

1

Это вариант на ответ Bungus', но вот один вкладыш, который решительно уродливее, но не распространяется Binding или что-нибудь:

foo = :bar 
baz = :bin 
hash = [:foo, :baz].inject({}) {|h, v| h[v] = eval(v.to_s); h } 
# hash => {:baz=>:bin, :foo=>:bar} 

Вы также можете сделать это выглядеть любопытное Сорта, как вызова метода злоупотребляя связывания блок - опять же, вариант на оригинальный ответ Bungus': (. Я просто говорю себе, что {[..]} символ уплотнитель мусора)

module Kernel 
    def compact(&block) 
    args = block.call.map &:to_sym 
    lvars = block.binding.send(:eval, "local_variables").map &:to_sym 
    (args & lvars).inject({}) do |h, v| 
     h[v] = block.binding.send(:eval, v.to_s); h 
    end 
    end 
end 

foo = :bar 
baz = :bin 
compact {[ :foo, :bar, :baz ]} 
# {:foo=>:bar, :baz=>:bin} 

Если вы используете binding_of_caller камень, вы можете отказаться от прок и явное связывание все вместе:

require 'binding_of_caller' 
module Kernel 
    def compact(*args) 
    lvars = binding.of_caller(1).send(:eval, "local_variables").map &:to_sym 
    (args.map(&:to_sym) & lvars).inject({}) do |h, v| 
     h[v] = binding.of_caller(2).send(:eval, v.to_s); h 
    end 
    end 
end 

foo = :bar 
baz = :bin 
compact :foo, :bar, :baz 
# {:foo=>:bar, :baz=>:bin} 

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