it-swarm-ru.tech

Скрытые возможности Ruby

Продолжая тему "Скрытые возможности ...", давайте расскажем о менее известных, но полезных возможностях языка программирования [Ruby].

Попробуйте ограничить это обсуждение ядром Ruby без всяких Ruby на Rails вещей.

Смотрите также:

(Пожалуйста, просто одну скрытую функцию за ответ.)

Спасибо

160
squadette

С Ruby 1.9 Proc # === является псевдонимом вызова Proc #, что означает, что объекты Proc можно использовать в операторах case, например, так:

def multiple_of(factor)
  Proc.new{|product| product.modulo(factor).zero?}
end

case number
  when multiple_of(3)
    puts "Multiple of 3"
  when multiple_of(7)
    puts "Multiple of 7"
end
80
Farrel

У Питера Купера есть хороший список трюков Ruby. Возможно, мой любимый из них - возможность перечисления как отдельных предметов, так и коллекций. (То есть обрабатывайте объект, не являющийся коллекцией, как коллекцию, содержащую только этот объект.) Это выглядит так:

[*items].each do |item|
  # ...
end
76
James A. Rosen

Не знаю, насколько это скрыто, но я нашел это полезным, когда нужно создать хеш из одномерного массива:

fruit = ["Apple","red","banana","yellow"]
=> ["Apple", "red", "banana", "yellow"]

Hash[*fruit]    
=> {"Apple"=>"red", "banana"=>"yellow"}
64
astronautism

Мне нравится один трюк - использовать расширитель splat (*) для объектов, отличных от Arrays. Вот пример соответствия регулярному выражению:

match, text, number = *"Something 981".match(/([A-z]*) ([0-9]*)/)

Другие примеры включают в себя:

a, b, c = *('A'..'Z')

Job = Struct.new(:name, :occupation)
tom = Job.new("Tom", "Developer")
name, occupation = *tom
54
tomafro

Вау, никто не упомянул оператор триггера:

1.upto(100) do |i|
  puts i if (i == 3)..(i == 15)
end
52
Konstantin Haase

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

Например, чтобы создать класс, который имеет неизвестный суперкласс до времени выполнения, то есть случайный, вы можете сделать следующее:

class RandomSubclass < [Array, Hash, String, Fixnum, Float, TrueClass].sample

end

RandomSubclass.superclass # could output one of 6 different classes.

Здесь используется метод 1.9 Array#sample (только в 1.8.7, см. Array#choice), и пример довольно надуманный, но вы можете увидеть всю мощь здесь.

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

def do_something_at(something, at = Time.now)
   # ...
end

Конечно, проблема с первым примером состоит в том, что он оценивается во время определения, а не во время вызова. Таким образом, после выбора суперкласса он остается этим суперклассом до конца программы.

Однако во втором примере каждый раз, когда вы вызываете do_something_at, переменная at будет временем вызова метода (ну, очень, очень близко к нему)

49
Bo Jeanes

Еще одна крошечная функция - конвертировать Fixnum в любую базу до 36:

>> 1234567890.to_s(2)
=> "1001001100101100000001011010010"

>> 1234567890.to_s(8)
=> "11145401322"

>> 1234567890.to_s(16)
=> "499602d2"

>> 1234567890.to_s(24)
=> "6b1230i"

>> 1234567890.to_s(36)
=> "kf12oi"

И, как прокомментировал Хью Уолтерс, преобразование в другую сторону так же просто:

>> "kf12oi".to_i(36)
=> 1234567890
47
tomafro

Хеши со значениями по умолчанию! Массив в этом случае.

parties = Hash.new {|hash, key| hash[key] = [] }
parties["Summer party"]
# => []

parties["Summer party"] << "Joe"
parties["Other party"] << "Jane"

Очень полезно в метапрограммировании.

40
August Lilleaas

Загрузите исходный код Ruby 1.9 и введите make golf, тогда вы можете сделать следующее:

make golf

./goruby -e 'h'
# => Hello, world!

./goruby -e 'p St'
# => StandardError

./goruby -e 'p 1.tf'
# => 1.0

./goruby19 -e 'p Fil.exp(".")'
"/home/manveru/pkgbuilds/Ruby-svn/src/trunk"

Прочитайте golf_prelude.c для более аккуратных вещей, скрывающихся.

39
manveru

Еще одно забавное дополнение в функциональности 1.9 Proc - это Proc # curry, который позволяет превратить Proc, принимающий n аргументов, в один, принимающий n-1. Здесь он сочетается с советом Proc # ===, о котором я упоминал выше:

it_is_day_of_week = lambda{ |day_of_week, date| date.wday == day_of_week }
it_is_saturday = it_is_day_of_week.curry[6]
it_is_sunday = it_is_day_of_week.curry[0]

case Time.now
when it_is_saturday
  puts "Saturday!"
when it_is_sunday
  puts "Sunday!"
else
  puts "Not the weekend"
end
38
Farrel

Булевы операторы на не булевых значениях.

&& и ||

Оба возвращают значение последнего оцененного выражения.

Вот почему ||= обновит переменную со значением, возвращаемым выражением справа, если переменная не определена. Это явно не задокументировано, но общеизвестно.

Однако &&= не так широко известен.

string &&= string + "suffix"

эквивалентно

if string
  string = string + "suffix"
end

Это очень удобно для деструктивных операций, которые не должны продолжаться, если переменная не определена.

35
EmFi

Функция Symbol # to_proc, которую обеспечивает Rails, действительно классная.

Вместо

Employee.collect { |emp| emp.name }

Ты можешь написать:

Employee.collect(&:name)
29
hoyhoy

И последнее: в Ruby вы можете использовать любой символ, который хотите разделить строки. Возьмите следующий код:

message = "My message"
contrived_example = "<div id=\"contrived\">#{message}</div>"

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

contrived_example = %{<div id="contrived-example">#{message}</div>}
contrived_example = %[<div id="contrived-example">#{message}</div>]

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

sql = %{
    SELECT strings 
    FROM complicated_table
    WHERE complicated_condition = '1'
}
28
tomafro

Я нахожу, что использование команды define_method для динамического создания методов довольно интересно и не так хорошо известно. Например:

((0..9).each do |n|
    define_method "press_#{n}" do
      @number = @number.to_i * 10 + n
    end
  end

Приведенный выше код использует команду define_method для динамического создания методов "press1" - "press9". Вместо того, чтобы вводить все 10 методов, которые по сути содержат один и тот же код, команда define method используется для генерации этих методов на лету по мере необходимости.

26
CodingWithoutComments

Используйте объект Range как бесконечный ленивый список:

Inf = 1.0 / 0

(1..Inf).take(5) #=> [1, 2, 3, 4, 5]

Более подробная информация здесь: http://banisterfiend.wordpress.com/2009/10/02/wtf-infinite-ranges-in-Ruby/

26
horseyguy

module_function

Методы модуля, объявленные как module_function , создадут свои копии как частные методы экземпляра в классе, который включает в себя модуль:

module M
  def not!
    'not!'
  end
  module_function :not!
end

class C
  include M

  def fun
    not!
  end
end

M.not!     # => 'not!
C.new.fun  # => 'not!'
C.new.not! # => NoMethodError: private method `not!' called for #<C:0x1261a00>

Если вы используете module_function без каких-либо аргументов, то любые методы модуля, которые идут после оператора module_function, автоматически становятся самими module_functions.

module M
  module_function

  def not!
    'not!'
  end

  def yea!
    'yea!'
  end
end


class C
  include M

  def fun
    not! + ' ' + yea!
  end
end
M.not!     # => 'not!'
M.yea!     # => 'yea!'
C.new.fun  # => 'not! yea!'
23
newtonapple

Коротко впрысните, вот так:

Сумма диапазона:

(1..10).inject(:+)
=> 55
23
user130730

Предупреждение: за этот элемент проголосовали # 1 Самый ужасный хак 2008 года , поэтому используйте с осторожностью. На самом деле, избегайте этого, как чумы, но это, безусловно, скрытый рубин.

Суператоры добавляют новых операторов в Ruby

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

Я не имею никакого отношения к Superators Project , кроме того, что восхищаюсь им.

21
Captain Hammer

Я опаздываю на вечеринку, но:

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

a = [:x, :y, :z]
b = [123, 456, 789]

Hash[a.Zip(b)]
# => { :x => 123, :y => 456, :z => 789 }

(Это работает, потому что Array # Zip "архивирует" значения из двух массивов:

a.Zip(b)  # => [[:x, 123], [:y, 456], [:z, 789]]

И Hash [] может взять именно такой массив. Я видел, как люди это делают:

Hash[*a.Zip(b).flatten]  # unnecessary!

Что дает тот же результат, но сплат и сплющение совершенно не нужны - возможно, их не было в прошлом?)

19
Jordan Running

Авто-живущие хэши в Ruby

def cnh # silly name "create nested hash"
  Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}
end
my_hash = cnh
my_hash[1][2][3] = 4
my_hash # => { 1 => { 2 => { 3 =>4 } } }

Это может быть чертовски удобно.

19
Trevoke

Разрушение массива

(a, b), c, d = [ [:a, :b ], :c, [:d1, :d2] ]

Куда:

a #=> :a
b #=> :b
c #=> :c
d #=> [:d1, :d2]

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

16
horseyguy

Class.new()

Создайте новый класс во время выполнения. Аргумент может быть производным классом, а блок является телом класса. Возможно, вы захотите взглянуть на const_set/const_get/const_defined?, чтобы правильно зарегистрировать новый класс, чтобы inspect печатало имя вместо числа.

Не то, что вам нужно каждый день, но довольно удобно, когда вы делаете.

15
Justin Love

создать массив последовательных чисел:

x = [*0..5]

устанавливает х в [0, 1, 2, 3, 4, 5]

13
horseyguy

Большая магия, которую вы видите в Rubyland, связана с метапрограммированием, то есть просто с написанием кода, который пишет код для вас. Ruby attr_accessor, attr_reader и attr_writer - все это простое метапрограммирование, поскольку они создают два метода в одной строке, следуя стандартному шаблону. Rails выполняет много метапрограммирования с помощью своих методов управления отношениями, таких как has_one и belongs_to.

Но довольно просто создать свои собственные приемы метапрограммирования, используя class_eval для выполнения динамически написанного кода.

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

class Wrapper
  attr_accessor :internal

  def self.forwards(*methods)
    methods.each do |method|
      define_method method do |*arguments, &block|
        internal.send method, *arguments, &block
      end
    end
  end

  forwards :to_i, :length, :split
end

w = Wrapper.new
w.internal = "12 13 14"
w.to_i        # => 12
w.length      # => 8
w.split('1')  # => ["", "2 ", "3 ", "4"]

Метод Wrapper.forwards принимает символы для имен методов и сохраняет их в массиве methods. Затем для каждого из этих данных мы используем define_method, чтобы создать новый метод, задачей которого является отправка сообщения, включая все аргументы и блоки.

Большой ресурс для проблем метапрограммирования Почему "Удачное метапрограммирование ясно" .

13
TALlama

используйте все, что отвечает ===(obj) для сравнения случаев:

case foo
when /baz/
  do_something_with_the_string_matching_baz
when 12..15
  do_something_with_the_integer_between_12_and_15
when lambda { |x| x % 5 == 0 }
  # only works in Ruby 1.9 or if you alias Proc#call as Proc#===
  do_something_with_the_integer_that_is_a_multiple_of_5
when Bar
  do_something_with_the_instance_of_Bar
when some_object
  do_something_with_the_thing_that_matches_some_object
end

Module (и, следовательно, Class), Regexp, Date и многие другие классы определяют метод экземпляра: === (other), и все они могут использоваться.

Спасибо Фаррел за напоминание о том, что Proc#call имеет псевдоним Proc#=== в Ruby 1.9.

12
James A. Rosen

Двоичный файл "Ruby" (по крайней мере, MRI) поддерживает множество переключателей, которые сделали Perl однострочным довольно популярным.

Значительные из них:

  • -n Устанавливает внешний цикл с помощью просто "get", который волшебным образом работает с данным именем файла или STDIN, устанавливая каждую строку чтения в $ _
  • -p Аналогично -n, но с автоматическими puts в конце каждой итерации цикла
  • -a Автоматический вызов .split в каждой строке ввода, хранится в $ F
  • -i Редактирование входных файлов на месте
  • -l Автоматический вызов .chomp при вводе
  • -e выполнить кусок кода
  • -c Проверить исходный код
  • -w с предупреждениями

Некоторые примеры:

# Print each line with its number:
Ruby -ne 'print($., ": ", $_)' < /etc/irbrc

# Print each line reversed:
Ruby -lne 'puts $_.reverse' < /etc/irbrc

# Print the second column from an input CSV (dumb - no balanced quote support etc):
Ruby -F, -ane 'puts $F[1]' < /etc/irbrc

# Print lines that contain "eat"
Ruby -ne 'puts $_ if /eat/i' < /etc/irbrc

# Same as above:
Ruby -pe 'next unless /eat/i' < /etc/irbrc

# Pass-through (like cat, but with possible line-end munging):
Ruby -p -e '' < /etc/irbrc

# Uppercase all input:
Ruby -p -e '$_.upcase!' < /etc/irbrc

# Same as above, but actually write to the input file, and make a backup first with extension .bak - Notice that inplace edit REQUIRES input files, not an input STDIN:
Ruby -i.bak -p -e '$_.upcase!' /etc/irbrc

Не стесняйтесь гуглить "Ruby one-liners" и "Perl one-liners" для множества более полезных и практических примеров. По сути, это позволяет вам использовать Ruby как довольно мощную замену awk и sed.

11
minaguib

Метод send () - это метод общего назначения, который можно использовать с любым классом или объектом в Ruby. Если не переопределено, send () принимает строку и вызывает имя метода, для которого она передана. Например, если пользователь нажимает кнопку "Clr", строка "press_clear" будет отправлена ​​методу send () и будет вызван метод "press_clear". Метод send () позволяет весело и динамично вызывать функции в Ruby.

 %w(7 8 9 / 4 5 6 * 1 2 3 - 0 Clr = +).each do |btn|
    button btn, :width => 46, :height => 46 do
      method = case btn
        when /[0-9]/: 'press_'+btn
        when 'Clr': 'press_clear'
        when '=': 'press_equals'
        when '+': 'press_add'
        when '-': 'press_sub'
        when '*': 'press_times'
        when '/': 'press_div'
      end

      number.send(method)
      number_field.replace strong(number)
    end
  end

Я больше говорю об этой функции в Blogging Shoes: The Simple-Calc Application

10
CodingWithoutComments
private unless Rails.env == 'test'
# e.g. a bundle of methods you want to test directly

Выглядит как крутой и (в некоторых случаях) приятный/полезный хак/особенность Ruby.

9
Szymon Jeż

Fixnum#to_s(base) может быть действительно полезным в некоторых случаях. Одним из таких случаев является генерация случайных (псевдо) уникальных токенов путем преобразования случайного числа в строку, используя основание 36.

Токен длины 8:

Rand(36**8).to_s(36) => "fmhpjfao"
Rand(36**8).to_s(36) => "gcer9ecu"
Rand(36**8).to_s(36) => "krpm0h9r"

Жетон длины 6:

Rand(36**6).to_s(36) => "bvhl8d"
Rand(36**6).to_s(36) => "lb7tis"
Rand(36**6).to_s(36) => "ibwgeh"
9
sickill

Обмани некоторый класс или модуль, говорящий, что он требует чего-то, что ему действительно не нужно:

$" << "something"

Это полезно, например, когда требуется A, который, в свою очередь, требует B, но нам не нужен B в нашем коде (и A не будет использовать его и в нашем коде):

Например, bdrb_test_helper requires'test/spec' Backgroundrb, но вы его вообще не используете, поэтому в вашем коде:

$" << "test/spec"
require File.join(File.dirname(__FILE__) + "/../bdrb_test_helper")
9
olegueret

Определение метода, который принимает любое количество параметров и просто отбрасывает их все

def hello(*)
    super
    puts "hello!"
end

Вышеупомянутому методу hello нужно только puts"hello" на экране и вызвать super - но поскольку суперкласс hello определяет параметры, которые он должен также использовать - однако, поскольку ему фактически не нужно использовать параметры - он не должен давать им имя.

9
horseyguy

Чтобы объединить несколько регулярных выражений с |, вы можете использовать

Regexp.union /Ruby\d/, /test/i, "cheat"

создать регулярное выражение, похожее на:

/(Ruby\d|[tT][eE][sS][tT]|cheat)/
8
J-_-L

Я считаю это полезным в некоторых сценариях. Это позволяет использовать переменные окружения напрямую, как в сценариях Shell и Makefiles. Переменные среды используются в качестве запасного варианта для неопределенных Ruby констант.

>> class <<Object
>>  alias :old_const_missing :const_missing
>>  def const_missing(sym)
>>   ENV[sym.to_s] || old_const_missing(sym)
>>  end
>> end
=> nil

>> puts Shell
/bin/zsh
=> nil
>> TERM == 'xterm'
=> true
8
Ropez

Как насчет открытия файла на основе ARGV [0]?

readfile.rb:

$<.each_line{|l| puts l}

Ruby readfile.rb testfile.txt

Это отличный способ для написания одноразовых скриптов. Существует целый набор предопределенных переменных, о которых большинство людей не знают. Используйте их с умом (читай: не засоряйте кодовую базу, которую вы планируете поддерживать с ними, это может запутаться).

8
Scott Holden

Я фанат:

%w{An Array of strings} #=> ["An", "Array", "of", "Strings"]

Забавно, как часто это полезно.

5
Judson

Несколько возвращаемых значений

def getCostAndMpg
    cost = 30000  # some fancy db calls go here
    mpg = 30
    return cost,mpg
end
AltimaCost, AltimaMpg = getCostAndMpg
puts "AltimaCost = #{AltimaCost}, AltimaMpg = #{AltimaMpg}"

Параллельное назначение

i = 0
j = 1
puts "i = #{i}, j=#{j}"
i,j = j,i
puts "i = #{i}, j=#{j}"

Виртуальные атрибуты

class Employee < Person
  def initialize(fname, lname, position)
    super(fname,lname)
    @position = position
  end
  def to_s
     super + ", #@position"
  end
  attr_writer :position
  def etype
     if @position == "CEO" || @position == "CFO"
         "executive"
     else
         "staff"
     end
  end
end
employee = Employee.new("Augustus","Bondi","CFO")
employee.position = "CEO"
puts employee.etype    =>  executive
employee.position = "Engineer"
puts employee.etype    =>  staff

method_missing - прекрасная идея

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

class MathWiz
  def add(a,b) 
    return a+b
  end
  def method_missing(name, *args)
    puts "I don't know the method #{name}"
  end
end
mathwiz = MathWiz.new
puts mathwiz.add(1,4)
puts mathwiz.subtract(4,2)

5

Я не знаю метод вычитания

ноль

5
Ramiz Uddin

Совет Джеймса Розена - это круто ([* items] .each), но я считаю, что он уничтожает хэши:

irb(main):001:0> h = {:name => "Bob"}
=> {:name=>"Bob"}
irb(main):002:0> [*h]
=> [[:name, "Bob"]]

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

irb(main):003:0> h = {:name => "Bob"}
=> {:name=>"Bob"}
irb(main):004:0> [h].flatten
=> [{:name=>"Bob"}]

Это можно сочетать с сигнатурой метода, например, так:

def process(*entries)
  [entries].flatten.each do |e|
    # do something with e
  end
end
4
minaguib

Я просто люблю встроенное ключевое слово спасение вот так:
РЕДАКТИРОВАННЫЙ ПРИМЕР:

@user #=> nil (but I did't know)
@user.name rescue "Unknown"
link_to( d.user.name, url_user( d.user.id, d.user.name)) rescue 'Account removed'

Это позволяет избежать взлома моего приложения и намного лучше, чем функция, выпущенная в Rails . Try ()

4
Fabiano Soriani

Вызов метода, определенного в любом месте цепочки наследования, даже если он переопределен

Объекты ActiveSupport иногда маскируются под встроенные объекты.

 требуют 'active_support' 
 days = 5.days 
 days.class # => Fixnum 
 days.is_a? (Fixnum) # => true 
 Fixnum === days # => false (да? Кто ты на самом деле?) 
 Object.instance_method (: class) .bind (days) .call # => ActiveSupport :: Duration (aha!) 
 ActiveSupport :: Duration === days # => true 

Выше, конечно, опирается на тот факт, что active_support не переопределяет Object # instance_method, и в этом случае мы действительно были бы в меру. Опять же, мы всегда можем сохранить возвращаемое значение Object.instance_method (: class) перед загрузкой любой сторонней библиотеки.

Object.instance_method (...) возвращает UnboundMethod, который затем можно привязать к экземпляру этого класса. В этом случае вы можете привязать его к любому экземпляру объекта (включая подклассы).

Если класс объекта включает модули, вы также можете использовать UnboundMethod из этих модулей.

 Модуль Mod 
 def var_add (подробнее); @ Вар + больше; end 
 end 
 Класс Cla 
 включает Mod 
 def initialize (var); @ Переменная = вар; end 
 # override 
 def var_add (more); @ Вар + еще + больше; end 
 end 
 cla = Cla.new ('abcdef') 
 cla.var_add ('ghi') # => "abcdefghighi" 
 Mod.instance_method ( : var_add) .bind (cla) .call ('ghi') # => "abcdefghi" 

Это даже работает для одноэлементных методов, которые переопределяют метод экземпляра класса, к которому принадлежит объект.

 класс Foo 
 def mymethod; "Оригинал"; end 
 end 
 foo = Foo.new 
 foo.mymethod # => 'original' 
 def foo.mymethod; "Синглтон"; end 
 foo.mymethod # => 'singleton' 
 Foo.instance_method (: mymethod) .bind (foo) .call # => 'original' 
 
 # Вы также можете вызвать метод #instance для одноэлементных классов: 
 Class << foo; самостоятельно; end.instance_method (: mymethod) .bind (foo) .call # => 'singleton' 
4
Kelvin

Есть некоторые аспекты символических литералов, которые люди должны знать. Один случай, решаемый с помощью специальных символьных литералов, - это когда вам нужно создать символ, имя которого по какой-то причине вызывает синтаксическую ошибку с обычным синтаксическим литералом символа:

:'class'

Вы также можете сделать интерполяцию символов. В контексте средства доступа, например:

define_method :"#{name}=" do |value|
  instance_variable_set :"@#{name}", value
end
3
Tom

each_with_index метод для любого перечисляемого объекта (массив, хэш и т. д.), возможно?

myarray = ["la", "li", "lu"]
myarray.each_with_index{|v,idx| puts "#{idx} -> #{v}"}

#result:
#0 -> la
#1 -> li
#2 -> lu

Возможно, он более известен, чем другие ответы, но не так хорошо известен всем Ruby программистам :)

3
mhd
class A

  private

  def my_private_method
    puts 'private method called'
  end
end

a = A.new
a.my_private_method # Raises exception saying private method was called
a.send :my_private_method # Calls my_private_method and prints private method called'
2
Chirantan

В Ruby есть механизм call/cc , позволяющий свободно перемещаться вверх и вниз по стеку.

Простой пример следует. Это, конечно, не то, как можно умножить последовательность в Ruby, но это демонстрирует, как можно использовать call/cc, чтобы добраться до стека, чтобы закоротить алгоритм. В этом случае мы рекурсивно умножаем список чисел, пока не увидим каждое число или не увидим ноль (два случая, когда мы знаем ответ). В нулевом случае мы можем быть сколь угодно глубокими в списке и завершаться.

#!/usr/bin/env Ruby

def rprod(k, rv, current, *nums)
  puts "#{rv} * #{current}"
  k.call(0) if current == 0 || rv == 0
  nums.empty? ? (rv * current) : rprod(k, rv * current, *nums)
end

def prod(first, *rest)
  callcc { |k| rprod(k, first, *rest) }
end

puts "Seq 1:  #{prod(1, 2, 3, 4, 5, 6)}"
puts ""
puts "Seq 2:  #{prod(1, 2, 0, 3, 4, 5, 6)}"

Вы можете увидеть результат здесь:

http://codepad.org/Oh8ddh9e

Для более сложного примера, показывающего продолжения, перемещающие другое направление в стеке, прочитайте источник в Генератор .

2
Dustin

Я просто прочитал все ответы ... одним заметным упущением было деструктурирующее задание:

> (a,b),c = [[1,2],3]
=> [[1,2],3]
> a
=> 1

Это также работает для параметров блока. Это полезно, когда у вас есть вложенные массивы, каждый элемент которых представляет что-то свое. Вместо написания кода, подобного "array [0] [1]", вы можете разбить этот вложенный массив и дать описательное имя каждому элементу в одной строке кода.

2
Alex D
@user #=> nil (but I did't know)
@user.name rescue "Unknown"
2
haoqi

Спринтф ярлык

Моя любимая функция Ruby. Синтаксис format_string % argument

"%04d"  % 1         # => "0001"
"%0.2f" % Math::PI  # => "3.14"

Работает также для массивов (format_string % array_of_arguments)

"%.2f %.3f %.4f" % ([Math::PI]*3) 
# => "3.14 3.142 3.1416"
1
iblue