"숨겨진 기능 ..."meme을 계속하면서, 루비 프로그래밍 언어의 덜 알려졌지만 유용한 기능을 공유해 보자.
Ruby on Rails를 사용하지 않고 핵심 Ruby로이 토론을 제한하십시오.
또한보십시오:
( 답변 당 하나의 숨겨진 기능 만 제공하십시오 .)
감사합니다
"숨겨진 기능 ..."meme을 계속하면서, 루비 프로그래밍 언어의 덜 알려졌지만 유용한 기능을 공유해 보자.
Ruby on Rails를 사용하지 않고 핵심 Ruby로이 토론을 제한하십시오.
또한보십시오:
( 답변 당 하나의 숨겨진 기능 만 제공하십시오 .)
감사합니다
답변:
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
피터 쿠퍼는이 좋은 목록 루비 트릭을. 아마도 내가 가장 좋아하는 것은 단일 항목과 컬렉션을 모두 열거하는 것입니다. 즉, 비 컬렉션 오브젝트는 해당 오브젝트 만 포함하는 콜렉션으로 취급합니다.
[*items].each do |item|
# ...
end
items
문자열 인 경우 [*…]로 묶을 필요가 없습니다. String.each는 예상대로 문자를 반복하지 않습니다. 그냥 블록으로 돌아갑니다.
이것이 어떻게 숨겨져 있는지 모르지만 1 차원 배열에서 해시를 만들어야 할 때 유용하다는 것을 알았습니다.
fruit = ["apple","red","banana","yellow"]
=> ["apple", "red", "banana", "yellow"]
Hash[*fruit]
=> {"apple"=>"red", "banana"=>"yellow"}
Hash[ [["apple","red"], ["banana","yellow"] ]
동일한 결과 를 생성합니다.
내가 좋아하는 트릭은 *
Array 이외의 객체 에서 splat ( ) 확장기 를 사용하는 것입니다. 정규식 일치에 대한 예는 다음과 같습니다.
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
text, number = *"text 555".match(/regexp/)[1..-1]
.
text, number = "Something 981".scan(/([A-z]*) ([0-9]*)/).flatten.map{|m| Integer(m) rescue m}
와우, 플립 플롭 연산자에 대해서는 아무도 언급하지 않았습니다 :
1.upto(100) do |i|
puts i if (i == 3)..(i == 15)
end
i == 3
false로 전환 후 i != 3
와 i == 15
. 플립 플롭과 유사 : en.wikipedia.org/wiki/Flip-flop_%28electronics%29
루비의 멋진 점 중 하나는 메소드 또는 클래스 정의와 같이 다른 언어가 찌푸린 곳에서 메소드를 호출하고 코드를 실행할 수 있다는 것입니다.
예를 들어, 런타임까지 알 수없는 수퍼 클래스가있는 클래스를 작성하려면 (예 : 임의) 다음을 수행하십시오.
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
변수는 메소드가 호출 된 시간이 될 것입니다 (물론, 아주 아주 가까운 그것에)
require 'activesupport'
또 다른 작은 기능- 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
String#to_s(base)
을 위해 정수로 다시 변환하는 데 사용할 수 있습니다. "1001001100101100000001011010010".to_i(2)
, "499602d2".to_i(16)
등 모든 반환 원래 Fixnum
.
기본값으로 해시! 이 경우 배열입니다.
parties = Hash.new {|hash, key| hash[key] = [] }
parties["Summer party"]
# => []
parties["Summer party"] << "Joe"
parties["Other party"] << "Jane"
메타 프로그래밍에 매우 유용합니다.
1.9 Proc 기능에 추가 된 또 다른 재미있는 기능은 Proc # curry입니다. 이것은 n 개의 인수를 수락하는 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
부울이 아닌 값에 대한 부울 연산자
&&
과 ||
둘 다 마지막으로 평가 된 식의 값을 반환합니다.
그렇기 때문에 ||=
변수가 정의되지 않은 경우 오른쪽에 반환 된 값으로 변수를 업데이트합니다. 이것은 명시 적으로 문서화되어 있지는 않지만 일반적인 지식입니다.
그러나 &&=
그다지 널리 알려지지 않았습니다.
string &&= string + "suffix"
에 해당
if string
string = string + "suffix"
end
변수가 정의되지 않은 경우 진행되지 않아야하는 파괴적인 작업에 매우 편리합니다.
string &&= string + "suffix"
는에 해당합니다 string = string && string + "suffix"
. 그건 &&
및 것은 ||
, 자신의 두 번째 인수가 곡괭이에서 설명 페이지를 반환합니다. 154 (1 부-루비의 양상, 표현, 조건부 실행).
Rails가 제공하는 Symbol # to_proc 함수는 정말 멋집니다.
대신에
Employee.collect { |emp| emp.name }
당신은 쓸 수 있습니다:
Employee.collect(&:name)
require 'activesupport'
실제로 대부분의 도우미가있는 곳이기 때문에 수행 할 수 있습니다 .
마지막으로 루비에서는 문자열을 구분하려는 모든 문자를 사용할 수 있습니다. 다음 코드를 사용하십시오.
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'
}
define_method 명령을 사용하여 흥미롭고 잘 알려지지 않은 메소드를 동적으로 생성하는 것을 발견했습니다. 예를 들면 다음과 같습니다.
((0..9).each do |n|
define_method "press_#{n}" do
@number = @number.to_i * 10 + n
end
end
위의 코드는 'define_method'명령을 사용하여 "press1"- "press9"메소드를 동적으로 작성합니다. essentailly가 동일한 코드를 포함하는 10 가지 메소드를 모두 입력하는 대신 define 메소드 명령을 사용하여 필요에 따라 이러한 메소드를 즉시 생성합니다.
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/
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!'
module_function
(2 방법) 만 사용하는 것입니다 extend self
(꽤 좋은 보이는 : D를)
require 'backports'
:-)
경고 :이 항목은 2008 년 1 위 Most Horrendous Hack 으로 선정되었습니다 . 사실, 전염병처럼 피하십시오.하지만 가장 확실한 것은 숨겨진 루비입니다.
코드에서 일부 고유 한 작업을위한 초 비밀 핸드 셰이크 연산자를 원하십니까? 코드 골프를하는 것처럼? -~ + ~-또는 <---와 같은 연산자를 사용하십시오. 마지막 연산자는 예제에서 항목의 순서를 반대로하는 데 사용됩니다.
나는 그것을 감탄하는 것 이상으로 수퍼 레이터 프로젝트 와 아무 관련이 없습니다 .
나는 파티에 늦었지만
두 개의 동일한 길이의 배열을 쉽게 가져 와서 하나의 배열로 키를 제공하고 다른 하나는 값을 해시로 전환 할 수 있습니다.
a = [:x, :y, :z]
b = [123, 456, 789]
Hash[a.zip(b)]
# => { :x => 123, :y => 456, :z => 789 }
이는 Array # zip이 두 배열의 값을 "zip"으로 만들기 때문에 작동합니다.
a.zip(b) # => [[:x, 123], [:y, 456], [:z, 789]]
그리고 Hash []는 그러한 배열을 취할 수 있습니다. 나는 사람들이 이것을하는 것을 보았습니다.
Hash[*a.zip(b).flatten] # unnecessary!
어느 결과가 같은 결과를 낼지 모르지만, 스 플랫과 플래 튼은 전적으로 불필요합니다. 아마도 과거에는 없었을까요?)
루비에서 자동 활성화 해시
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 } } }
이것은 단지 편리 할 수 있습니다.
module InfHash; def self.new; Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}; end; end
Class.new()
런타임에 새 클래스를 작성하십시오. 인수는 파생 클래스가 될 수 있으며 블록은 클래스 본문입니다. 또한보고 할 수 있습니다 const_set/const_get/const_defined?
, 새로운 클래스가 제대로 등록하려면 그래서 inspect
이름 대신 번호를 출력합니다.
매일 필요한 것은 아니지만 할 때 매우 편리합니다.
MyClass = Class.new Array do; def hi; 'hi'; end; end
에 해당하는 것 같습니다 class MyClass < Array; def hi; 'hi'; end; end
.
연속적인 숫자의 배열을 만듭니다.
x = [*0..5]
x를 [0, 1, 2, 3, 4, 5]로 설정
*
) 연산자는 기본적으로 to_a
어쨌든 호출 합니다.
Rubyland에서 볼 수있는 많은 마법은 메타 프로그래밍과 관련이 있습니다. 메타 프로그래밍은 단순히 코드를 작성하는 코드를 작성하는 것입니다. 루비의 attr_accessor
, attr_reader
, 그리고 attr_writer
그들이 표준 패턴 다음 한 줄에 두 가지 방법을 생성한다는 점에서, 모든 단순 메타 프로그래밍입니다. Rails는 has_one
and와 같은 관계 관리 방법을 사용하여 많은 메타 프로그래밍을 수행합니다 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의 "메타 프로그래밍을 분명히 보는 이유" 입니다.
===(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
(기타) === 모두 사용될 수있다, 다른 많은 클래스 인스턴스 메소드를 정의한다.
Ruby 1.9에서 와 같이 별칭이 지정 되었음을 상기시켜 준 Farrel 에게 감사합니다 .Proc#call
Proc#===
"루비"바이너리 (적어도 MRI)는 perl one-liner를 매우 대중적으로 만든 많은 스위치를 지원합니다.
중요한 것들 :
put
각 루프 반복 끝에 자동 s가 있습니다.몇 가지 예 :
# 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
더 유용하고 실용적인 톤을 찾기 위해 Google "루비 원 라이너"및 "펄 원 라이너"를 자유롭게 사용하십시오. 본질적으로 루비를 awk와 sed의 상당히 강력한 대체물로 사용할 수 있습니다.
전송 () 메소드는 루비의 모든 클래스 나 객체에 사용할 수있는 범용 방법이다. 재정의되지 않으면 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 에서 더 자세히 설명합니다 .
실제로 필요하지 않은 것이 필요하다고 말하는 클래스 또는 모듈을 속이십시오.
$" << "something"
이것은 예를 들어 A를 요구할 때 유용하지만 B에는 필요하지만 코드에는 B가 필요하지 않습니다 (그리고 A는 코드를 통해 그것을 사용하지 않을 것입니다).
예를 들어 Backgroundrb 's bdrb_test_helper requires
'test/spec'
이지만 코드에서 전혀 사용하지 않습니다.
$" << "test/spec"
require File.join(File.dirname(__FILE__) + "/../bdrb_test_helper")
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"