2013-08-02 3 views
2

В CoffeeScript:Захват переменных в методах Ruby,

f = -> 
    v = 5 
    g = -> 
    v 
    g() 

f() # returns 5 as expected 

В Ruby:

def f 
    v = 5 
    def g 
    v # undefined local variable or method `v' for main:Object (NameError) 
    end 
    g 
end 
f 

Итак, по-видимому, JavaScript функции созданы для захвата переменных в рамках которого они созданы, но Руби методов нет. Есть ли способ заставить Ruby-методы вести себя как функции JavaScript в этом отношении?

+1

Рубин не имеет функции. Он имеет методы и имеет блоки. Методы имеют новые области применения, блоки имеют вложенные области. –

+0

@ JörgWMittag На самом деле, как я узнал из ответа Фреда, Ruby имеет методы и ** замыкания **. Блоки - это всего лишь один из трех видов замыканий в Ruby. Есть также Лямбдас и Прок. – anthropomorphic

+2

Lambdas и Procs - это просто объекты, которые создаются путем передачи блоков методам. (Или с использованием синтаксиса лямбда-литерала.) –

ответ

5

Ruby имеет область действия сценария, область определения модуля/класса/метода и область блока. Только блоки создают вложенные области. Итак, вам нужно использовать блок для определения вашего метода. К счастью, есть способ для определения методов, которые принимают блок:

def f 
    v = 5 
    define_method :g do 
    v 
    end 
    g 
end 
f 
# => 5 

Однако, обратите внимание, что это делает не делать то, что вы думаете, что делает (и ни один не делает исходный код). Он не определяет способ g, вложенный в метод f. Ruby не имеет вложенных методов. Методы всегда принадлежат модулям (классы - это модули), они не могут принадлежать методам.

Это способ определения метода f, который при запуске определяет метод g, а затем вызывает этот метод.

Наблюдайте:

methods.include?(:g) 
# => true 

вы определили новый метод верхнего уровня (на самом деле частный метод экземпляра Object) под названием g, и вы будете определять его снова и снова и снова, каждый раз f получает называется.

То, что вы, вероятно, хотите, лямбда:

def f 
    v = 5 
    g = -> { v } 
    g.() 
end 
f 
# => 5 

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

Ладно, так что я возиться с лямбды, и я заметил, что все Я делаю до v внутри g не отражается в v раз g возвращается. Могу ли я внести изменения в палку v?

def f 
    v = 5 
    g = -> { v = 'Hello' } 
    g.() 
    v 
end 
f 
# => 'Hello' 

Как вы можете видеть, изменение vделает "кнут" после g возвращается.

+0

Итак, g.() Эквивалентно g.call и g.call()! Я не знал этого. – Fred

+0

@Fred, И есть g []. В документе ProC# [] упоминается синтаксис g.() И указывается, что g.() Является синтаксическим сахаром для g.call. Мац должен быть действительно отчаянным, чтобы сохранить 2 символа набора текста! Но кому нравится приближаться к этим скобкам? И вы все равно должны ударить по смену, так что вы даже не сохраняете никаких штрихов! На мой взгляд, это действительно уродливый синтаксис, и я не буду его использовать. – 7stud

+0

Я удалил этот комментарий, потому что понял, что у меня есть для * вызова * 'g'. Но спасибо за исправление. – anthropomorphic

8

Вы действительно не определить методы внутри методов в Ruby, но вы можете использовать lambda:

def f 
    v = 5 

    g = lambda do 
    v 
    end 

    g.call 
end 
+0

И 'g.call' вернет' 5', а не выбросит ошибку? – anthropomorphic

+0

@ антропоморфный Да, он вернется 5. [Как вы можете видеть.] (Http://repl.it/JuQ) –

+0

@anthropomorphic Скопируйте и вставьте, затем запустите его в IRB ... – cmpolis

2

Короткий ответ, нет. Функции Ruby имеют разные правила определения области действия, чем функции Javascript.

Более длинный ответ заключается в том, что вы можете определять объекты в Ruby, которые сохраняют область, в которой они определены (называются замыканием). Они называются lambdas, Procs и блоки в Ruby. Посмотрите here для краткого обзора.

Редактировать: блоки не являются объектами Ruby, но их можно преобразовать в Procs, которые являются объектами.

+1

Просьба пояснить нижний предел, чтобы я мог узнать, что было ошибкой. Благодарю. – Fred

+1

Вероятно, нижний предел был для этих «функций Рубина», так как строго никаких функций в Ruby нет, только методы и блоки. – tokland

1

Чтобы добавить другие ответы, для большей согласованности с вашим кодом CoffeeScript, вы бы, вероятно, написать:

f = lambda do 
    v = 5 

    g = lambda do 
    v 
    end 

    g[] 
end 
f[] 
1

Замыкание это именованный блок кода (функции в некоторых языках, лямбда или процедурный в Ruby) со следующими характеристиками:

  • она запоминает значения всех переменных, которые были доступны в объеме она была определена (внешней области), даже при вызове на другой области или когда эти переменные больше не являются Availab le (вне сферы действия).
  • Это объект. Следовательно, его можно присвоить переменной, пройденной вокруг, вызванной из разных областей и т. Д.

Ниже приведен пример использования Лямбды. То же самое можно сделать с помощью Proc.

minutes = 40 
def meditate minutes 
    return lambda { minutes } 
end 

p = meditate minutes 
minutes = nil 
p.call 

# Output: 
=> 40 

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

Ruby имеет два типа затворов: Lambdas и Procs.

Методы - это не замыкания, и объекты метода не являются. Как заявил Юкихиро Мацумото (Matz) в своей книге «Язык программирования Ruby»:

Важное различие между объектами метода и объектами Proc заключается в том, что объекты метода не являются замыканиями. Метод Ruby должен быть полностью автономным, и у них никогда не будет доступа к локальным переменным за пределами их возможностей. Таким образом, единственным связыванием, сохраненным объектом Method, является значение self - объект, на который должен быть вызван метод.

Смотри на this post about Methods in the Zen Ruby blog.

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