Скрытые функции Ruby

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

Попытайтесь ограничить это обсуждение с базовым Ruby без любого материала Ruby on Rails.

См. также:

(Всего одна скрытая функция на ответ.)

Спасибо

160
задан 7 revs, 5 users 32% 23 May 2017 в 10:31
поделиться

31 ответ

От Ruby 1.9 Proc# === псевдоним к Proc#call, что означает, что объекты Proc могут использоваться в случае, если операторы как так:

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
ответ дан 4 revs, 3 users 79% 23 November 2019 в 21:27
поделиться

Эти отправляют () , метод является методом общего назначения, который может использоваться на любом Классе или Объекте в Ruby. Если не переопределенный, отправьте (), принимает строку и называет название метода, строка которого оно передается. Например, если пользователь нажмет кнопку “Clr”, строка ‘press_clear’ будет отправлена в отправление (), метод и ‘press_clear’ метод назовут. Отправление () метод допускает забавный и динамический способ вызвать функции в 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

я говорю больше об этой функции в Ведущая блог Обувь: Простое-Calc Приложение

10
ответ дан 2 revs 23 November 2019 в 21:27
поделиться

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

$" << "something"

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

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

$" << "test/spec"
require File.join(File.dirname(__FILE__) + "/../bdrb_test_helper")
9
ответ дан olegueret 23 November 2019 в 21:27
поделиться

используйте что-либо, что отвечает ===(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, и много других классов определяют метод экземпляра: === (другой), и может все использоваться.

Благодаря Farrel для напоминания Proc#call искажаемый как Proc#=== в Ruby 1.9.

12
ответ дан 3 revs 23 November 2019 в 21:27
поделиться

Большое волшебство, которое Вы видите в Rubyland, имеет отношение к метапрограммированию, которое просто пишет код, который пишет код для Вас. Ruby attr_accessor, attr_reader, и attr_writer является всем простым метапрограммированием, в этом они создают два метода в одной строке, после стандартного шаблона. Направляющие делают много метапрограммирования с их методами управления отношений как 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 для создания нового метода, задание которого он должен отправить сообщению вперед, включая все аргументы и блоки.

А большой ресурс для проблем метапрограммирования Почему "Наблюдение Lucky Stiff Метапрограммирования Очевидно" .

13
ответ дан 3 revs, 3 users 84% 23 November 2019 в 21:27
поделиться

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

[*items].each do |item|
  # ...
end
76
ответ дан James A. Rosen 23 November 2019 в 21:27
поделиться

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

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

Hash[*fruit]    
=> {"apple"=>"red", "banana"=>"yellow"}
64
ответ дан astronautism 23 November 2019 в 21:27
поделиться

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

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
ответ дан 2 revs, 2 users 92% 23 November 2019 в 21:27
поделиться

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

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

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
ответ дан 4 revs, 4 users 91% 23 November 2019 в 21:27
поделиться

Другая крошечная функция - преобразовывает 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"

И поскольку Huw Walters прокомментировал, преобразовывание другого пути так же просто:

>> "kf12oi".to_i(36)
=> 1234567890
47
ответ дан 3 revs, 2 users 97% 23 November 2019 в 21:27
поделиться

Загрузите источник 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"

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

39
ответ дан manveru 23 November 2019 в 21:27
поделиться

Другое забавное дополнение в 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
ответ дан 2 revs 23 November 2019 в 21:27
поделиться

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

Вместо

Employee.collect { |emp| emp.name }

можно записать:

Employee.collect(&:name)
29
ответ дан hoyhoy 23 November 2019 в 21:27
поделиться

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

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>]

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

sql = %{
    SELECT strings 
    FROM complicated_table
    WHERE complicated_condition = '1'
}
28
ответ дан tomafro 23 November 2019 в 21:27
поделиться

Я нахожу, что использование команда 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 методов, которые по существу содержат тот же код, определить команда метода используется для генерации этих методов на лету по мере необходимости.

26
ответ дан 2 revs 23 November 2019 в 21:27
поделиться

методы 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
ответ дан 2 revs 23 November 2019 в 21:27
поделиться

Предупреждение: за этот объект проголосовали № 1 Самый Ужасающий Взлом 2008 , таким образом используйте с осторожностью. На самом деле избегайте его как чумы, но это - несомненно Скрытый Ruby.

Superators Добавляют, что Новые Операторы к Ruby

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

я не имею никакого отношения Проект Superators вне восхищения им.

21
ответ дан Captain Hammer 23 November 2019 в 21:27
поделиться

Class.new()

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

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

15
ответ дан Justin Love 23 November 2019 в 21:27
поделиться

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

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

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

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

40
ответ дан 23 November 2019 в 21:27
поделиться

Короткое внедрение, например:

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

(1..10).inject(:+)
=> 55
23
ответ дан 23 November 2019 в 21:27
поделиться

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

x = [*0..5]

устанавливает x равным [0, 1, 2, 3, 4, 5]

13
ответ дан 23 November 2019 в 21:27
поделиться

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

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

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!

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

19
ответ дан 23 November 2019 в 21:27
поделиться

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

Inf = 1.0 / 0

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

Подробнее здесь: http://banisterfiend.wordpress.com/2009/10/02/wtf-infinite- range-in-ruby /

26
ответ дан 23 November 2019 в 21:27
поделиться

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

&& и ||

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

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

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

string &&= string + "suffix"

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

if string
  string = string + "suffix"
end

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

35
ответ дан 23 November 2019 в 21:27
поделиться

Двоичный "рубин" (по крайней мере, МРТ) поддерживает множество переключателей, что сделало perl one-liners весьма популярным.

Значимые:

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

-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

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

11
ответ дан 23 November 2019 в 21:27
поделиться

Автовидифицирующий хэши в 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
ответ дан 23 November 2019 в 21:27
поделиться

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

Простой пример следует. Это, конечно, не, как можно было бы умножить последовательность в рубине, но он демонстрирует, как можно было бы использовать 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 23 November 2019 в 21:27
поделиться

Как насчет того, чтобы открыть файл на основе ARGV [0]?

readfile.rb:

$<.each_line{|l| puts l}

ruby readfile.rb testfile.txt

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

8
ответ дан 2 revs, 2 users 84% 23 November 2019 в 21:27
поделиться
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
ответ дан 23 November 2019 в 21:27
поделиться

Я считаю это полезным в некоторых скриптах. Это позволяет напрямую использовать переменные среды, например, в сценариях оболочки и Makefile. Переменные среды используются как резерв для неопределенных констант 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
ответ дан 23 November 2019 в 21:27
поделиться