คุณสมบัติที่ซ่อนของ Ruby


160

ดำเนินการต่อ "meme คุณสมบัติที่ซ่อนอยู่ของ ... " มาแบ่งปันคุณสมบัติที่รู้จักกันน้อย แต่มีประโยชน์ของภาษาการเขียนโปรแกรม Ruby

พยายาม จำกัด การสนทนานี้ด้วย Core Ruby โดยไม่มี Ruby on Rails

ดูสิ่งนี้ด้วย:

(โปรดเพียงหนึ่งคุณสมบัติที่ซ่อนอยู่ต่อคำตอบ)

ขอบคุณ


ควรเป็นวิกิชุมชน
SilentGhost

คำตอบ:


80

จาก Ruby 1.9 Proc # === เป็นนามแฝงถึง Proc # call ซึ่งหมายความว่าวัตถุ 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

1
ที่จริงฉันเขียนพลอยในจุดหนึ่งเพื่อทำสิ่งนี้ แต่รหัสของฉันคือ (a) ความยุ่งเหยิงและ (b) ช้า ฉันดีใจมากที่ฟังก์ชั่นดังกล่าวกลายเป็นแกนหลัก
James A. Rosen

76

Peter Cooper มีรายการเคล็ดลับ Ruby ที่ดี บางทีสิ่งที่ฉันโปรดปรานของเขาคืออนุญาตให้แจกแจงรายการและคอลเลกชันเดี่ยว (นั่นคือถือว่าวัตถุที่ไม่ใช่คอลเลกชันเป็นคอลเลกชันที่มีเพียงวัตถุนั้น) มันมีลักษณะเช่นนี้:

[*items].each do |item|
  # ...
end

38
รูปแบบที่ชัดเจนยิ่งขึ้น (และดีกว่า) คือ Array (รายการ)
.each

หากitemsเป็นสตริงคุณไม่จำเป็นต้องล้อมด้วย [* …] String.each ไม่ซ้ำไปตามตัวอักษรอย่างที่บางคนคาดไว้ มันเพิ่งกลับมาสู่บล็อก
mxcl

สิ่งนี้จะใช้ประโยชน์อะไรเคย? แค่สงสัย.
Ed S.

1
@Ed: ดีถ้าคุณกำลังเขียนวิธีการและต้องการอนุญาตให้ผู้ใช้วิธีการส่งรายการ varargs หรือ Array
James A. Rosen

64

ไม่ทราบว่าซ่อนไว้อย่างไร แต่ฉันพบว่ามีประโยชน์เมื่อต้องการทำให้แฮชเป็นอาร์เรย์หนึ่งมิติ:

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

Hash[*fruit]    
=> {"apple"=>"red", "banana"=>"yellow"}

โปรดทราบว่าHash[ [["apple","red"], ["banana","yellow"] ]ก่อให้เกิดผลลัพธ์เดียวกัน
Marc-André Lafortune

54

หนึ่งในเคล็ดลับที่ฉันชอบคือการใช้*ตัวขยาย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

13
บังเอิญสำหรับคนที่อยากรู้อยากเห็นสิ่งนี้ทำงานโดยปริยายโทร to_a กับเป้าหมายของแดง
Bob Aman

1
text, number = *"text 555".match(/regexp/)[1..-1]หากคุณไม่ได้สนใจในการแข่งขันคุณสามารถมี
Andrew Grimm

text, number = "Something 981".scan(/([A-z]*) ([0-9]*)/).flatten.map{|m| Integer(m) rescue m}
Jonas Elfström

7
ทั้งเทคนิคที่ดี แต่ต้องมีจุดที่มันวิเศษมากใช่มั้ย!
tomafro

1
@Andrew คุณคิดว่าการจับคู่นั้นสามารถกลับมาเป็นศูนย์หรือไม่? ไม่มีวิธีการ []
Alexey

52

ว้าวไม่มีใครพูดถึงตัวดำเนินการของฟล็อปฟล็อป:

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

11
ใช่ ... ใครบางคนจะต้องอธิบายเรื่องนี้ให้ฉันฟัง มันใช้งานได้ แต่ฉันไม่สามารถหาสาเหตุได้
Bob Aman

12
ตัวดำเนินการ flip flop เป็น statefull ถ้า สถานะของสวิทช์ให้เป็นจริงโดยเร็วi == 3และสวิทช์เป็นเท็จหลัง และi != 3 i == 15คล้ายกับ flip-flop: en.wikipedia.org/wiki/Flip-flop_%28electronics%29
Konstantin Haase

1
ฉันจะไม่เรียกสิ่งนี้ว่าเป็นคุณลักษณะที่ซ่อนอยู่ ฉันจำได้ว่าครั้งแรกที่ฉันได้รู้จักกับ #Ruby ใน Freenode เมื่อหลายปีก่อน ฉันเคยใช้คุณสมบัติทุกอย่างของ Ruby ในบางจุดยกเว้นคุณสมบัตินี้
ELLIOTTCABLE

1
ฉันจะไม่เรียกมันว่าเป็นเรื่องน่ารำคาญ แต่เป็นสิ่งที่คุณไม่ได้ใช้ ฉันใช้มันและมันสามารถลดโค้ดได้อย่างดีโดยเฉพาะอย่างยิ่งเมื่อฉันคว้าบล็อกบรรทัดจากไฟล์ตามเกณฑ์บางอย่าง
Tin Man

49

หนึ่งในสิ่งที่ยอดเยี่ยมเกี่ยวกับทับทิมคือคุณสามารถเรียกใช้เมธอดและเรียกใช้โค้ดในที่ ๆ ภาษาอื่น ๆ จะขมวดคิ้วเช่นในเมธอดหรือนิยามคลาส

ตัวอย่างเช่นในการสร้างคลาสที่มีซูเปอร์คลาสที่ไม่รู้จักจนกระทั่งถึงเวลาใช้งานนั่นคือสุ่มคุณสามารถทำสิ่งต่อไปนี้:

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-only, see Array#choice) และตัวอย่างนั้นถูกออกแบบมาอย่างสวยงาม แต่คุณสามารถเห็นพลังได้ที่นี่

อีกตัวอย่างที่ยอดเยี่ยมคือความสามารถในการใส่ค่าพารามิเตอร์เริ่มต้นที่ไม่คงที่ (เช่นภาษาอื่นมักต้องการ):

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

แน่นอนปัญหากับตัวอย่างแรกก็คือมันจะถูกประเมินตามเวลาที่กำหนดไม่ใช่เวลาโทร ดังนั้นเมื่อเลือกซูเปอร์คลาสแล้วจะยังคงเป็นซูเปอร์คลาสนั้นตลอดเวลาที่เหลือของโปรแกรม

อย่างไรก็ตามในตัวอย่างที่สองทุกครั้งที่คุณโทรหาdo_something_atที่atตัวแปรจะเป็นเวลาที่วิธีการที่เรียกว่า (ดีมากใกล้เคียงกับมัน)


2
หมายเหตุ: Array # rand จัดทำโดย ActiveSupport ซึ่งคุณสามารถใช้ภายนอก Rails ได้อย่างง่ายดายrequire 'activesupport'
rfunduk

Array # choice อยู่ใน 1.8.7
Josh Lee

24
Array # choice คือ 1.8.7 เท่านั้น ! อย่าใช้มันมันหายไปใน 1.9 และจะหายไปใน 1.8.8 ใช้ #sample
Marc-André Lafortune

python: คลาส DictList ([dict, รายการ] [random.randint (0,1)]): pass
Anurag Uniyal

def do_something_at (บางสิ่งบางอย่างที่ = แลมบ์ดา {Time.now}) at.call #now กำหนดเวลาสิ้นสุดแบบไดนามิก
Jack Kinsella

47

อีกคุณสมบัติเล็ก ๆ - แปลงเป็น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

1
และเพื่อความสมบูรณ์String#to_s(base)สามารถใช้ในการแปลงกลับเป็นจำนวนเต็ม; "1001001100101100000001011010010".to_i(2), "499602d2".to_i(16)ฯลฯ Fixnumทั้งหมดกลับมาที่เดิม
Huw Walters

40

แฮชที่มีค่าเริ่มต้น! อาร์เรย์ในกรณีนี้

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

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

มีประโยชน์มากใน metaprogramming


1
ใช่จริง Ruby hash สามารถยอมรับโอเปอเรเตอร์ '<<' หากมีการกำหนดค่าเริ่มต้นไว้แล้วด้วย '=' (ไม่สนใจแม้ว่าจะเป็นการกำหนดที่ว่างเปล่า) มิฉะนั้นแฮชจะไม่ยอมรับ '<<' CMIIW
mhd

39

ดาวน์โหลดแหล่งทับทิม 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สิ่งที่ซ่อนเร้นมากขึ้น


38

การเพิ่มความสนุกใน 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

35

ตัวดำเนินการบูลีนบนค่าที่ไม่ใช่บูลีน

&& และ ||

ทั้งสองคืนค่าของนิพจน์สุดท้ายที่ประเมิน

ซึ่งเป็นสาเหตุที่||=จะอัปเดตตัวแปรพร้อมกับนิพจน์ที่ส่งคืนค่าทางด้านขวาหากตัวแปรนั้นไม่ได้กำหนด นี่ไม่ใช่เอกสารอย่างชัดเจน แต่เป็นความรู้ทั่วไป

อย่างไรก็ตามเรื่อง&&=นี้ไม่ค่อยเป็นที่รู้จักอย่างกว้างขวางนัก

string &&= string + "suffix"

เทียบเท่ากับ

if string
  string = string + "suffix"
end

มันมีประโยชน์มากสำหรับการทำลายที่ไม่ควรดำเนินการหากตัวแปรไม่ได้กำหนด


2
อีกอย่างแม่นยำ เทียบเท่ากับstring &&= string + "suffix" string = string && string + "suffix"นั่น&&และ||กลับอาร์กิวเมนต์ที่สองของพวกเขาจะกล่าวถึงในพลั่วพี 154 (ตอนที่ 1 - แง่มุมของทับทิม, นิพจน์, การดำเนินการตามเงื่อนไข)
Richard Michael

29

ฟังก์ชั่น Symbol # to_proc ที่ Rails มีนั้นยอดเยี่ยมจริงๆ

แทน

Employee.collect { |emp| emp.name }

คุณสามารถเขียน:

Employee.collect(&:name)

เห็นได้ชัดว่านี่คือ "ลำดับความสำคัญช้ากว่า" มากกว่าการใช้บล็อก igvita.com/2008/07/08/6-optimization-tips-for-ruby-mri
Charles Roper

ฉันเพิ่งลองและพบว่าไม่มีความแตกต่างอย่างมีนัยสำคัญระหว่างทั้งสอง ฉันไม่แน่ใจว่าสิ่งที่ "ลำดับความสำคัญ" นี้มาจากไหน (ใช้ Ruby 1.8.7)
Matt Grande

1
การทำสิ่งนี้นอก Rails นั้นมีประโยชน์และสามารถทำได้require 'activesupport'เนื่องจากเป็นจริงที่ผู้ช่วยเหลือเหล่านี้ส่วนใหญ่มาจาก
rfunduk

8
สิ่งนี้เคยทำช้าเพราะ implemenetation ของ active_support นั่นคือยอมรับข้อโต้แย้งหลาย ๆ ข้อเพื่อให้คุณสามารถทำ cool shit เช่น (1..10) .inject &: *, แต่กรณีการใช้งานหลักมักจะเรียกเมธอดบนสมาชิกแต่ละคนของ คอลเลกชันเช่น% w (จิ้งจอกสีน้ำตาลด่วน). แผนที่และ: upcase ตั้งแต่ 1.8.7 เป็นทับทิมทับทิมหลักและประสิทธิภาพมีความสมเหตุสมผล
Steve Graham

4
@thenduks: และสามารถทำได้โดยไม่ต้องขอความช่วยเหลือจาก activesupport ใน ruby ​​1.8.7 และ 1.9
Andrew Grimm

28

หนึ่งตัวสุดท้าย - ในทับทิมคุณสามารถใช้อักขระใด ๆ ที่คุณต้องการคั่นสตริง ใช้รหัสต่อไปนี้:

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'
}

19
ไม่ใช่ตัวละครใด ๆแต่ก็ยังค่อนข้างเท่ห์ นอกจากนี้ยังทำงานกับตัวอักษรอื่น ๆ :% () /% {} /% [] /% <> /% || % r () /% r {} /% r [] /% r <> /% r || % w () /% w {} /% w [] /% w <> /% w ||
Bo Jeanes

นอกจากนี้ยังมีรูปแบบเอกสาร doc ต่อไปนี้: << บล็อก ... บล็อกซึ่งฉันต้องการใช้สำหรับสิ่งต่าง ๆ เช่นคำสั่ง SQL หลายบรรทัดเป็นต้น
มาร์ตินตัน

26

ฉันพบว่าการใช้คำสั่ง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 วิธีซึ่ง essentailly มีรหัสเดียวกันคำสั่ง define method จะใช้ในการสร้างวิธีการเหล่านี้ได้ทันทีตามต้องการ


4
ปัญหาเดียวของ define_method คือมันไม่อนุญาตให้ส่งบล็อกเป็นพารามิเตอร์ใน ruby ​​1.8 ดูโพสต์บล็อกนี้สำหรับวิธีแก้ปัญหา
Andrew Grimm

26

ใช้วัตถุ Range เป็นรายการ lazy ที่ไม่มีที่สิ้นสุด:

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/


lazy_select ในบทความที่เชื่อมโยงนั้นประณีตมาก
Joseph Weissman

มันยอดเยี่ยมจริงๆ ฉันชอบวิธีที่ไม่มีที่สิ้นสุดลอยเป็นวิธีที่เมื่อฉันลองนี้: (-Inf..Inf) .take (4) มันยก (สอดคล้องกันในเชิงตรรกะ) ไม่สามารถย้ำจากข้อผิดพลาดลอย : D
zachaysan

23

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!'

4
หากคุณต้องการประกาศวิธีส่วนตัวในโมดูลเพียงใช้คำหลักส่วนตัว นอกเหนือจากการทำให้เมธอดเป็นส่วนตัวในคลาสที่มีโมดูลแล้ว module_function จะคัดลอกเมธอดไปยังอินสแตนซ์โมดูล ในกรณีส่วนใหญ่นี่ไม่ใช่สิ่งที่คุณต้องการ
tomafro

ฉันรู้ว่าคุณสามารถใช้ส่วนตัวได้ แต่นี่เป็นคำถามเกี่ยวกับคุณสมบัติที่ซ่อนของรูบี้ และฉันคิดว่าคนส่วนใหญ่ไม่เคยได้ยิน module_function (รวมอยู่ด้วยตัวเอง) จนกว่าพวกเขาจะเห็นมันในเอกสารและเริ่มเล่นกับมัน
newtonapple

ทางเลือกในการใช้module_function(วิธีที่ 2) คือการใช้extend self(ซึ่งดูดีมาก: D)
J -_- L


21

คำเตือน: ไอเท็มนี้ได้รับการโหวต # 1 แฮ็คที่น่ากลัวที่สุดในปี 2008ดังนั้นควรใช้ด้วยความระมัดระวัง ที่จริงแล้วหลีกเลี่ยงมันเหมือนกับโรคระบาด แต่เป็นทับทิมซ่อนแน่นอนที่สุด

Superators เพิ่มตัวดำเนินการใหม่ไปยัง Ruby

เคยต้องการตัวดำเนินการจับมือลับสุดยอดสำหรับการดำเนินการที่ไม่ซ้ำกันในรหัสของคุณหรือไม่ ชอบเล่นรหัสกอล์ฟไหม? ลองใช้โอเปอเรเตอร์เช่น - ~ + ~ - หรือ <--- อันสุดท้ายถูกใช้ในตัวอย่างสำหรับการกลับคำสั่งของรายการ

ฉันไม่มีอะไรเกี่ยวข้องกับโครงการ Superatorsเกินกว่าจะชื่นชมมัน


19

ฉันไปงานปาร์ตี้สาย แต่:

คุณสามารถใช้อาร์เรย์ที่มีความยาวเท่ากันสองชุดได้อย่างง่ายดายและเปลี่ยนเป็นแฮชโดยมีหนึ่งอาร์เรย์ที่จัดหาคีย์และค่าอื่น ๆ :

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

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

(ใช้งานได้เนื่องจาก Array # zip "zips" ค่าสูงสุดจากสองอาร์เรย์:

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

และ Hash [] สามารถรับอาร์เรย์ได้ ฉันเคยเห็นคนทำเช่นนี้:

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

ซึ่งให้ผลเหมือนกัน แต่แผ่นสีและแบนไม่จำเป็นทั้งหมด - บางทีพวกเขาไม่ได้เป็นในอดีต?)


3
นี่เป็นสิ่งที่ไม่มีเอกสารมานานแล้ว (ดูredmine.ruby-lang.org/issues/show/1385 ) โปรดทราบว่ารูปแบบใหม่นี้เป็นของใหม่สำหรับ Ruby 1.8.7
Marc-André Lafortune

19

แฮชอัตโนมัติใน 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 } } }

สิ่งนี้สามารถเป็นประโยชน์ได้


1
ฉันจะรวมไว้ในหน่วยความจำเพื่อให้มีความรู้สึกเช่นเดียวกับแฮชเนทีฟแบบดั้งเดิม:module InfHash; def self.new; Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}; end; end
asaaki

16

การทำลายอาร์เรย์

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

ที่ไหน:

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

การใช้เทคนิคนี้เราสามารถใช้การมอบหมายอย่างง่าย ๆ เพื่อรับค่าที่แน่นอนที่เราต้องการจากอาร์เรย์ที่มีความลึกหลายระดับ


15

Class.new()

สร้างคลาสใหม่ ณ เวลาทำงาน อาร์กิวเมนต์สามารถเป็นคลาสที่ได้มาจากและบล็อกเป็นคลาสของร่างกาย คุณอาจต้องการดูconst_set/const_get/const_defined?การลงทะเบียนคลาสใหม่ของคุณอย่างถูกต้องเพื่อinspectพิมพ์ชื่อแทนหมายเลข

ไม่ใช่สิ่งที่คุณต้องการทุกวัน แต่มีประโยชน์เมื่อคุณทำ


1
MyClass = Class.new Array do; def hi; 'hi'; end; endclass MyClass < Array; def hi; 'hi'; end; endดูเหมือนว่าจะเทียบเท่ากับ
yfeldblum

1
น่าจะเป็นจริงมากกว่าที่ฉันคิด แม้ปรากฏว่าคุณสามารถสืบทอดจากตัวแปรมากกว่าค่าคงที่เท่านั้น อย่างไรก็ตามรุ่น sugared (ที่สอง) จะไม่ทำงานหากคุณต้องการสร้างชื่อคลาส ณ เวลาทำงาน (การถอดชุด eval แน่นอน)
Justin Love

เทคนิคนี้จะอธิบายสวยดีในหนังสือmetaprogramming ทับทิม
Paul Pladijs

13

สร้างอาร์เรย์ของตัวเลขที่ต่อเนื่องกัน:

x = [*0..5]

ตั้งค่า x เป็น [0, 1, 2, 3, 4, 5]


ใช่ แต่มันไม่สั้นและหวาน;)
horseyguy

2
รวบรัดเป็นไปตามวัตถุประสงค์การอ่านเป็นเรื่องของรสชาติและประสบการณ์
Alexey

แดง ( *ผู้ดำเนินการ) โดยทั่วไปเรียกร้องto_aอยู่แล้ว
Matheus Moreira

13

ความมหัศจรรย์มากมายที่คุณเห็นใน Rubyland เกี่ยวข้องกับ metaprogramming ซึ่งเป็นการเขียนโค้ดที่เขียนโค้ดให้คุณ Ruby's attr_accessor,, attr_readerและattr_writermetaprogramming ทั้งหมดนั้นพวกเขาสร้างสองวิธีในหนึ่งบรรทัดตามรูปแบบมาตรฐาน Rails ใช้เมตาโพรไฟล์โปรแกรมจำนวนมากด้วยวิธีการจัดการความสัมพันธ์เช่นhas_oneและbelongs_toและ

แต่มันค่อนข้างง่ายที่จะสร้างเทคนิคการ metaprogramming ของคุณเองโดยใช้class_evalเพื่อรันโค้ดที่เขียนขึ้นแบบไดนามิก

ตัวอย่างต่อไปนี้อนุญาตให้วัตถุ wrapper ส่งต่อวิธีการบางอย่างพร้อมกับวัตถุภายใน:

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เพื่อสร้างวิธีการใหม่ที่มีหน้าที่ส่งข้อความรวมถึงอาร์กิวเมนต์และบล็อกทั้งหมด

แหล่งข้อมูลที่ดีสำหรับปัญหา metaprogramming คือทำไมโชคดีแข็งของ "เห็น metaprogramming เห็นได้ชัด"


ฉันต้องการที่จะดำน้ำก่อนเข้าสู่การเปรียบเทียบโปรแกรมในทับทิม คุณสามารถให้ข้อมูลอ้างอิงเพื่อเริ่มต้นกับมัน (นอกเหนือจากลิงค์ที่ให้)? หนังสือก็จะทำเช่นกัน ขอบคุณ
Chirantan

ชุดวิดีโอของ PragProg "The Ruby Object Model และ Metaprogramming" เป็นคำแนะนำที่ดีสำหรับการเขียนโปรแกรมเมตาโดยใช้ ruby: pragprog.com/screencasts/v-dtrubyom/ …
caffo

@Chirantan, มีลักษณะที่metaprogramming ทับทิม
Paul Pladijs

12

ใช้อะไรก็ได้ที่ตอบสนองต่อการ===(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


11

ไบนารี "ruby" (อย่างน้อย MRI) สนับสนุนสวิตช์จำนวนมากที่ทำให้ Perl หนึ่ง liners ได้รับความนิยมมาก

คนสำคัญ:

  • -n ตั้งค่าวงนอกด้วยเพียงแค่ "รับ" - ซึ่งทำงานได้อย่างน่าอัศจรรย์กับชื่อไฟล์ที่กำหนดหรือ STDIN การตั้งค่าแต่ละบรรทัดอ่านใน $ _
  • -p คล้ายกับ -n แต่มีputs อัตโนมัติที่ตอนท้ายของการวนซ้ำแต่ละรอบ
  • - เรียกอัตโนมัติเพื่อ. แยกในแต่ละบรรทัดอินพุตเก็บไว้ใน $ F
  • -i ในสถานที่แก้ไขไฟล์อินพุต
  • -l อัตโนมัติโทรไปยัง. chomp ที่อินพุต
  • - ดำเนินการชิ้นส่วนของรหัส
  • -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

อย่าลังเลที่จะ google "ruby one-liners" และ "perl one-liners" สำหรับตัวอย่างที่ใช้งานได้จริงและใช้งานได้จริง มันช่วยให้คุณสามารถใช้ทับทิมเป็นสิ่งทดแทนที่ทรงพลังพอสมควร


10

วิธีการส่ง ()เป็นวิธีการทั่วไปที่สามารถใช้กับ Class หรือ Object ใดก็ได้ใน Ruby หากไม่แทนที่จะส่ง () ยอมรับสตริงและเรียกชื่อของเมธอดที่ส่งผ่านสตริง ตัวอย่างเช่นหากผู้ใช้คลิกปุ่ม“ 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

ฉันพูดคุยเกี่ยวกับคุณสมบัตินี้ในบล็อกของรองเท้า: แอปพลิเคชัน Simple-Calc


ฟังดูเป็นวิธีที่ดีในการเปิดช่องโหว่
mP

4
ฉันจะใช้สัญลักษณ์ทุกที่ที่เป็นไปได้
reto

9

หลอกคลาสหรือโมดูลที่บอกว่าต้องการบางสิ่งที่ไม่จำเป็นต้องใช้:

$" << "something"

สิ่งนี้มีประโยชน์เช่นเมื่อต้องการ A ที่ผลัดกันต้องใช้ B แต่เราไม่ต้องการ B ในรหัสของเรา (และ A จะไม่ใช้ B ผ่านรหัสของเรา):

ตัวอย่างเช่น Backgroundrb's bdrb_test_helper requires 'test/spec'แต่คุณไม่ได้ใช้เลยในรหัสของคุณ:

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

สิ่งนี้แก้ไขปัญหาที่ gem A ต้องการ foo-1.0.0 หรือไม่และ gem B ต้องการ foo-1.0.1 หรือไม่
Andrew Grimm

ไม่เพราะไม่มีรหัส "บางอย่าง": นี่เป็นเพียงการจำลองว่าจำเป็นต้องใช้ "บางสิ่ง" แต่มันไม่จำเป็นต้องใช้จริงๆ $ "เป็นอาร์เรย์ที่มีชื่อโมดูลที่โหลดโดยต้องการ (มันถูกใช้โดยต้องการเพื่อป้องกันการโหลดโมดูลสองครั้ง) ดังนั้นหากคุณใช้สิ่งนี้เพื่อหลอกอัญมณีที่จะสร้างความผิดพลาดเมื่ออัญมณีพยายามที่จะใช้" บางสิ่งบางอย่าง "ที่แท้จริง รหัสเพราะมันไม่ได้มีอยู่คุณอาจต้องการบังคับให้ laoding อัญมณีรุ่นที่เป็นรูปธรรม (เช่น foo-1.0.0) แทนที่จะเป็นรหัสล่าสุด: docs.rubygems.org/read/chapter/4#page71
olegueret

9

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

การกำหนดวิธีการที่ยอมรับพารามิเตอร์จำนวนเท่าใดก็ได้และละทิ้งพวกเขาทั้งหมด

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

helloวิธีการข้างต้นจำเป็นต้องputs "hello"อยู่บนหน้าจอและการโทรsuperเท่านั้น - แต่เนื่องจากซูเปอร์คลาสhelloกำหนดพารามิเตอร์มันก็ต้องมีเช่นกัน - อย่างไรก็ตามเนื่องจากมันไม่จำเป็นต้องใช้พารามิเตอร์เอง - มันไม่จำเป็นต้องตั้งชื่อให้กับพวกเขา

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.