Ruby Gotchas ที่มือใหม่ควรได้รับการเตือนคืออะไร? [ปิด]


108

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

x = true and false
puts x  # displays true!

และมีชื่อเสียง:

puts "zero is true!" if 0  # zero is true!

"Gotchas" อื่น ๆ ที่คุณจะเตือนมือใหม่ Ruby คืออะไร?


@ phrases.insert (0, p) OK @ phrases.insert (p) ไม่มีอะไรเกิดขึ้น @phrases << p # OK
Anno2001

ทำไมtrue and falseกลับจริง?
Jürgen Paul

3
เนื่องจาก "x = true and false" ถูกตีความเป็น "(x = true) และ false" เป็นเรื่องของความสำคัญของตัวดำเนินการ: "และ" มีลำดับความสำคัญต่ำกว่า "=" ภาษาอื่น ๆ ส่วนใหญ่มีลำดับความสำคัญย้อนกลับฉันไม่รู้ว่าทำไมพวกเขาถึงเลือกคำสั่งนี้ใน Rails ฉันคิดว่ามันสับสนมาก หากคุณต้องการลักษณะการทำงาน "ปกติ" เพียงพิมพ์ "x = (จริงและเท็จ)" จากนั้น x จะเป็นเท็จ
MiniQuark

4
อีกวิธีหนึ่งคือการใช้ "&&" และ "||" แทนที่จะเป็น "และ" และ "หรือ": มีพฤติกรรมตามที่คาดไว้ ตัวอย่างเช่น "x = true && false" ส่งผลให้ x เป็นเท็จ
MiniQuark

“ หลักการประหลาดใจอย่างน้อยที่สุดหมายถึงหลักการที่ทำให้ฉันประหลาดใจอย่างน้อยที่สุด” จากen.wikipedia.org/wiki/Ruby_(programming_language)#Philosophyเช่นเดียวกันกับ Python ฉันมีคำพูดที่คล้ายกันเกี่ยวกับผู้สร้าง Python แต่ฉันลืมว่าอยู่ที่ไหน
Darek Nędza

คำตอบ:


59

วิกิพีเดีย Ruby gotchas

จากบทความ:

  • ชื่อที่ขึ้นต้นด้วยอักษรตัวใหญ่จะถือว่าเป็นค่าคงที่ดังนั้นตัวแปรท้องถิ่นควรขึ้นต้นด้วยอักษรตัวพิมพ์เล็ก
  • อักขระ$และ@ไม่ได้ระบุชนิดข้อมูลตัวแปรเช่นเดียวกับใน Perl แต่ทำหน้าที่เป็นตัวดำเนินการแก้ไขขอบเขต
  • ในการแสดงตัวเลขทศนิยมหนึ่งต้องตามด้วยเลขศูนย์ ( 99.0) หรือการแปลงอย่างชัดเจน ( 99.to_f) การต่อท้ายจุด ( 99.) ไม่เพียงพอเนื่องจากตัวเลขมีความอ่อนไหวต่อไวยากรณ์ของวิธีการ
  • การประเมินผลแบบบูลของข้อมูลที่ไม่ใช่แบบบูลเป็นที่เข้มงวด: 0, ""และได้รับการประเมินเพื่อ[] trueใน C นิพจน์0 ? 1 : 0จะประเมินเป็น0(เช่นเท็จ) อย่างไรก็ตามใน Ruby ให้ผลตอบแทน1ตามที่ตัวเลขทั้งหมดประเมินtrueไว้ เท่านั้นnilและประเมินผลfalse falseข้อสรุปของกฎนี้คือวิธีการของ Ruby ตามแบบแผน - ตัวอย่างเช่นการค้นหานิพจน์ทั่วไป - ส่งกลับตัวเลขสตริงรายการหรือค่าอื่น ๆ ที่ไม่ใช่เท็จสำหรับความสำเร็จ แต่nilเมื่อล้มเหลว (เช่นไม่ตรงกัน) อนุสัญญานี้ยังใช้ใน Smalltalk ซึ่งมีเฉพาะวัตถุพิเศษtrueและfalseสามารถใช้ในนิพจน์บูลีนได้
  • เวอร์ชันก่อน 1.9 ไม่มีชนิดข้อมูลอักขระ (เปรียบเทียบกับ C ซึ่งระบุประเภทcharสำหรับอักขระ) สิ่งนี้อาจทำให้เกิดความประหลาดใจเมื่อหั่นสตริง: "abc"[0]ผลตอบแทน97(จำนวนเต็มแทนรหัส ASCII ของอักขระตัวแรกในสตริง); เพื่อขอรับการ"a"ใช้งาน"abc"[0,1](สตริงย่อยที่มีความยาว 1) หรือ"abc"[0].chr.
  • สัญกรณ์statement until expressionซึ่งแตกต่างจากงบเทียบเท่าภาษาอื่น ๆ (เช่นdo { statement } while (not(expression));ใน C / C ++ / ... ), trueจริงไม่เคยทำงานคำสั่งถ้าการแสดงออกที่มีอยู่แล้ว นี่เป็นเพราะstatement until expressionน้ำตาลที่เป็นไวยากรณ์มากกว่า

    until expression
      statement
    end

    ซึ่งการเทียบเท่าใน C / C ++ while (not(expression)) statement;ก็เหมือนกับstatement if expressionเทียบเท่ากับ

    if expression
      statement
    end

    อย่างไรก็ตามสัญกรณ์

    begin
      statement
    end until expression

    ใน Ruby จะรันคำสั่งหนึ่งครั้งแม้ว่านิพจน์นั้นจะเป็นจริงอยู่แล้วก็ตาม

  • เนื่องจากค่าคงที่เป็นการอ้างอิงถึงอ็อบเจ็กต์การเปลี่ยนค่าคงที่อ้างถึงจึงสร้างคำเตือน แต่การปรับเปลี่ยนอ็อบเจ็กต์เองไม่ได้ ตัวอย่างเช่นGreeting << " world!" if Greeting == "Hello"ไม่สร้างข้อผิดพลาดหรือคำเตือน สิ่งนี้คล้ายกับfinalตัวแปรใน Java แต่ Ruby ยังมีฟังก์ชันในการ "ตรึง" วัตถุซึ่งแตกต่างจาก Java

คุณสมบัติบางอย่างที่แตกต่างจากภาษาอื่น ๆ :

  • ผู้ประกอบการตามปกติสำหรับการแสดงออกเงื่อนไขandและorไม่ปฏิบัติตามกฎระเบียบปกติของความเป็นผู้นำ: ไม่ได้ผูกแน่นกว่าand orRuby ยังมีตัวดำเนินการนิพจน์||และ&&ทำงานตามที่คาดไว้

  • defภายในdefไม่ทำในสิ่งที่โปรแกรมเมอร์ Python คาดหวัง:

    def a_method
        x = 7
        def print_x; puts x end
        print_x
    end

    สิ่งนี้ทำให้เกิดข้อผิดพลาดเกี่ยวกับการxไม่กำหนด คุณต้องใช้ไฟล์Proc.

คุณสมบัติของภาษา

  • การละเว้นวงเล็บรอบอาร์กิวเมนต์เมธอดอาจทำให้ได้ผลลัพธ์ที่ไม่คาดคิดหากเมธอดใช้พารามิเตอร์หลายตัว ผู้พัฒนา Ruby ได้ระบุว่าการละเว้นวงเล็บของวิธีการแบบหลายพารามิเตอร์อาจไม่ได้รับอนุญาตในเวอร์ชัน Ruby ในอนาคต ล่าม Ruby ในปัจจุบัน (พฤศจิกายน 2550) ส่งคำเตือนที่กระตุ้นให้ผู้เขียนไม่ละเว้น()เพื่อหลีกเลี่ยงความหมายที่คลุมเครือของรหัส ไม่ได้ใช้ก็ยังคงปฏิบัติร่วมกันและสามารถที่จะมีความสุขโดยเฉพาะอย่างยิ่งที่จะใช้ทับทิมเป็นคนอ่านเฉพาะโดเมนภาษาการเขียนโปรแกรมของตัวเองพร้อมกับวิธีการที่เรียกว่า()method_missing()

1
Ruby 1.9 ไม่มีประเภทข้อมูลอักขระด้วย ใน 1.8 ตัวดำเนินการดัชนีส่งคืน Fixnum ใน 1.9 เทียบเท่ากับการแบ่งสตริงอักขระหนึ่งตัว
whitequark

38

มือใหม่จะมีปัญหากับวิธีการเสมอภาค :

  • a == b : ตรวจสอบว่า a และ b เท่ากันหรือไม่ นี่คือประโยชน์สูงสุด
  • a.eql? b : ตรวจสอบด้วยว่า a และ b เท่ากันหรือไม่ แต่บางครั้งก็เข้มงวดกว่า (อาจตรวจสอบว่า a และ b มีประเภทเดียวกันเป็นต้น) ส่วนใหญ่จะใช้ในแฮช
  • ก. เท่ากัน? b : ตรวจสอบว่า a และ b เป็นวัตถุเดียวกันหรือไม่ (การตรวจสอบเอกลักษณ์)
  • a === b : ใช้ใน case statement (ฉันอ่านว่า " a match b ")

ตัวอย่างเหล่านี้ควรชี้แจง 3 วิธีแรก:

a = b = "joe"

a==b       # true
a.eql? b   # true
a.equal? b # true (a.object_id == b.object_id)

a = "joe"
b = "joe"

a==b       # true
a.eql? b   # true
a.equal? b # false (a.object_id != b.object_id)

a = 1
b = 1.0

a==b       # true
a.eql? b   # false (a.class != b.class)
a.equal? b # false

โปรดทราบว่า == , eql? และเท่าเทียมกัน? ควรสมมาตรเสมอ: ถ้า a == b แล้ว b == a

สังเกตด้วยว่า==และeql? ทั้งสองถูกนำไปใช้ในคลาส Object เป็นนามแฝงเพื่อให้เท่ากัน? ดังนั้นหากคุณสร้างชั้นเรียนใหม่และต้องการ ==และeql? เพื่อหมายถึงสิ่งอื่นที่ไม่ใช่ตัวตนธรรมดาคุณต้องลบล้างทั้งสองอย่าง ตัวอย่างเช่น:

class Person
    attr_reader name
    def == (rhs)
      rhs.name == self.name  # compare person by their name
    end
    def eql? (rhs)
      self == rhs
    end
    # never override the equal? method!
end

===วิธีการทำงานแตกต่างกัน ก่อนอื่นมันไม่ใช่สมมาตร (a === b ไม่ได้หมายความว่า b === a) อย่างที่บอกคุณสามารถอ่าน a === b เป็น "a match b" นี่คือตัวอย่างบางส่วน:

# === is usually simply an alias for ==
"joe" === "joe"  # true
"joe" === "bob"  # false

# but ranges match any value they include
(1..10) === 5        # true
(1..10) === 19       # false
(1..10) === (1..10)  # false (the range does not include itself)

# arrays just match equal arrays, but they do not match included values!
[1,2,3] === [1,2,3] # true
[1,2,3] === 2       # false

# classes match their instances and instances of derived classes
String === "joe"   # true
String === 1.5     # false (1.5 is not a String)
String === String  # false (the String class is not itself a String)

กรณีคำสั่งจะขึ้นอยู่กับ===วิธีการ:

case a
  when "joe": puts "1"
  when 1.0  : puts "2"
  when (1..10), (15..20): puts "3"
  else puts "4"
end

เทียบเท่ากับสิ่งนี้:

if "joe" === a
  puts "1"
elsif 1.0 === a
  puts "2"
elsif (1..10) === a || (15..20) === a
  puts "3"
else
  puts "4"
end

หากคุณกำหนดคลาสใหม่ที่มีอินสแตนซ์แทนคอนเทนเนอร์หรือช่วงบางประเภท (หากมีบางอย่างเช่นวิธีการinclude?หรือการจับคู่? ) คุณอาจพบว่ามีประโยชน์ในการแทนที่===วิธีการเช่นนี้:

class Subnet
  [...]
  def include? (ip_address_or_subnet)
    [...]
  end
  def === (rhs)
    self.include? rhs
  end
end

case destination_ip
  when white_listed_subnet: puts "the ip belongs to the white-listed subnet"
  when black_listed_subnet: puts "the ip belongs to the black-listed subnet"
  [...]
end

1
นอกจากนี้: a = 'строка'; b = 'строка'; ต่อปี == b; a = a.force_encoding 'ASCII-8BIT'; b = b.force_encoding 'UTF-8'; ต่อปี == b; ต่อ === b; พี a.eql? ข; p ก. เท่ากัน? b
Nakilon

20
  • ปะลิง Ruby มีคลาสแบบเปิดดังนั้นพฤติกรรมของพวกเขาจึงสามารถเปลี่ยนแปลงได้แบบไดนามิกที่รันไทม์ ...

  • ออบเจ็กต์อาจตอบสนองต่อวิธีการที่ไม่ได้กำหนดหากmethod_missingหรือsendถูกลบล้าง นี่เป็นการใช้ประโยชน์จากการร้องขอวิธีการตามข้อความของ Ruby ระบบActiveRecordของRailsใช้สิ่งนี้เพื่อให้ได้ผลดี


18

รหัสต่อไปนี้ทำให้ฉันประหลาดใจ ฉันคิดว่ามันเป็น gotcha ที่อันตรายทั้งง่ายต่อการเจอและแก้ไขจุดบกพร่องได้ยาก

(1..5).each do |number|
  comment = " is even" if number%2==0
  puts number.to_s + comment.to_s
end

สิ่งนี้พิมพ์:

1
2 is even
3
4 is even
5

แต่ถ้าฉันเพิ่งเพิ่มcomment =อะไรก่อนบล็อก ...

comment = nil
(1..5).each do |number|
  comment = " is even" if number%2==0
  puts number.to_s + comment.to_s
end

จากนั้นฉันจะได้รับ:

1
2 is even
3 is even
4 is even
5 is even

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

ทางออกหนึ่งคือเขียนสิ่งนี้แทน:

comment = number%2==0 ? " is even" : nil

ฉันคิดว่าหลาย ๆ คน (รวมทั้งฉัน) มักจะเขียนว่า " a = b if c" แทนที่จะเป็น " a = (c ? b : nil)" เพราะมันอ่านง่ายกว่า แต่เห็นได้ชัดว่ามันมีผลข้างเคียง


4
คุณอาจเงาตัวแปรขอบเขตภายนอกด้วย (1..5) do | number; comment | ..... อ่านที่นี่stackoverflow.com/questions/1654637/…
Özgür

6
สิ่งนี้ดูเหมือนจะมีเหตุผลสำหรับฉัน การกำหนดขอบเขตนี้เป็นเรื่องปกติของภาษาอื่นเป็นเพียงไวยากรณ์ที่แตกต่างกัน
ก.

อย่างไรก็ตามคุณสามารถเขียนa = (b if c)เพื่อให้ได้เอฟเฟกต์ที่ต้องการโดยไม่ต้องใช้เทอร์นารี นี่เป็นเพราะการb if cประเมินเป็นศูนย์ถ้าcเป็นเท็จ
Cameron Martin

16

เมื่อเรียกsuperโดยไม่มีอาร์กิวเมนต์เมธอดที่ถูกลบล้างจะถูกเรียกด้วยอาร์กิวเมนต์เดียวกันกับเมธอดการลบล้าง

class A
  def hello(name="Dan")
    puts "hello #{name}"
  end
end

class B < A
  def hello(name)
    super
  end
end

B.new.hello("Bob") #=> "hello Bob"

ในการโทรไปจริงมีการขัดแย้งใดที่คุณต้องการที่จะพูดsupersuper()


3
หากB#helloมีname = 42ก่อนหน้าจะมีsuperข้อความว่า "สวัสดี 42"
Andrew Grimm

14

บล็อกและวิธีการส่งคืนค่าของบรรทัดสุดท้ายตามค่าเริ่มต้น การเพิ่มputsข้อความต่อท้ายเพื่อวัตถุประสงค์ในการแก้ไขจุดบกพร่องอาจทำให้เกิดผลข้างเคียงที่ไม่พึงประสงค์



11

ฉันมีปัญหาอย่างมากในการทำความเข้าใจตัวแปรคลาสแอตทริบิวต์คลาสและวิธีการคลาส รหัสนี้อาจช่วยมือใหม่:

class A
  @@classvar = "A1"
  @classattr = "A2"
  def self.showvars
    puts "@@classvar => "+@@classvar
    puts "@classattr => "+@classattr
  end
end

A.showvars
  # displays:
  # @@classvar => A1
  # @classattr => A2

class B < A
  @@classvar = "B1"
  @classattr = "B2"
end

B.showvars
  # displays:
  # @@classvar => B1
  # @classattr => B2

A.showvars
  # displays:
  # @@classvar => B1   #Class variables are shared in a class hierarchy!
  # @classattr => A2   #Class attributes are not

1
ใช่ตัวแปรคลาสอาจเป็นเรื่องยุ่งยาก ฉันคิดว่า Rubyists ที่มีประสบการณ์ส่วนใหญ่จะบอกว่าควรหลีกเลี่ยงเพราะโดยปกติจะมีวิธีอื่นในการแก้ปัญหาโดยไม่มีพวกเขา ผู้ที่ชื่นชอบภาษาบางคนอาจกล่าวว่าตัวแปรชั้นเรียนของ Ruby ได้รับการออกแบบมาไม่ดีในระดับภาษา
David J.

8

สิ่งหนึ่งที่ฉันได้เรียนรู้คือการใช้ตัวดำเนินการ || = อย่างระมัดระวัง และดูแลเป็นพิเศษหากคุณกำลังจัดการกับบูลีน ฉันมักจะใช้ a || = b เป็นตัวจับทั้งหมดเพื่อให้ 'a' เป็นค่าเริ่มต้นหากทุกอย่างล้มเหลวและ 'a' ยังคงเป็นศูนย์ แต่ถ้า a เป็นเท็จและ b เป็นจริง a จะถูกกำหนดให้เป็นจริง


คุณสามารถใช้a = b if a.nil?หรือ@a = b unless defined?(@a).
Andrew Grimm

8
  • บล็อกมีความสำคัญมากในการทำความเข้าใจพวกมันถูกใช้ทุกที่

  • คุณไม่จำเป็นต้องมีวงเล็บรอบ ๆ พารามิเตอร์วิธีการ ไม่ว่าคุณจะใช้หรือไม่ขึ้นอยู่กับคุณ บางคนบอกว่าคุณควรใช้พวกเขา

  • ใช้การยกและช่วยในการจัดการข้อยกเว้นไม่ใช่โยนและจับ

  • คุณสามารถใช้ได้;แต่ไม่จำเป็นต้องทำเว้นแต่คุณต้องการใส่หลาย ๆ อย่างในบรรทัดเดียว


หากคุณไม่ได้วางแผนที่จะไปไกลกว่า Ruby 1.8.6 ให้ละเว้น parens เท่าที่คุณต้องการ มิฉะนั้นคุณอาจจะดีกว่าที่จะใช้มัน
Mike Woodhouse

7

ฉันมีปัญหากับมิกซ์อินที่มีวิธีการอินสแตนซ์และคลาสเมธอด รหัสนี้อาจช่วยมือใหม่:

module Displayable
  # instance methods here
  def display
    puts name
    self.class.increment_displays
  end
  def self.included(base)
    # This module method will be called automatically
    # after this module is included in a class.
    # We want to add the class methods to the class.
    base.extend Displayable::ClassMethods
  end
  module ClassMethods
    # class methods here
    def number_of_displays
      @number_of_displays # this is a class attribute
    end
    def increment_displays
      @number_of_displays += 1
    end
    def init_displays
      @number_of_displays = 0
    end
    # this module method will be called automatically
    # after this module is extended by a class.
    # We want to perform some initialization on a
    # class attribute.
    def self.extended(base)
      base.init_displays
    end
  end
end

class Person
  include Displayable
  def name; @name; end
  def initialize(name); @name=name; end
end

puts Person.number_of_displays # => 0
john = Person.new "John"
john.display # => John
puts Person.number_of_displays # => 1
jack = Person.new "Jack"
jack.display # => Jack
puts Person.number_of_displays # => 2

ตอนแรกฉันคิดว่าฉันสามารถมีโมดูลที่มีทั้งวิธีการอินสแตนซ์และวิธีคลาสโดยทำสิ่งนี้:

module Displayable
  def display
    puts name
    self.class.increment_displays
  end
  def self.number_of_displays  # WRONG!
    @number_of_displays
  end
  [...]
end

น่าเสียดายที่ method number_of_displaysจะไม่ถูกรวมหรือขยายเนื่องจากเป็น "module class method" เฉพาะ "วิธีการอินสแตนซ์โมดูล" เท่านั้นที่สามารถรวมไว้ในคลาส (เป็นวิธีการของอินสแตนซ์) หรือขยายลงในคลาส (เป็นวิธีการของคลาส) นี่คือเหตุผลที่คุณต้องใส่วิธีการอินสแตนซ์ของ mixin ลงในโมดูลและเมธอดคลาสของ mixin ของคุณในโมดูลอื่น (คุณมักจะใส่เมธอดคลาสไว้ในโมดูลย่อย "ClassMethods") ขอบคุณที่รวมวิธีมายากล, คุณสามารถทำให้มันง่ายที่จะรวมทั้งวิธีการเช่นและวิธีการเรียนในเวลาเพียงหนึ่งง่าย "รวมถึงสามารถแสดง" โทร (ดังแสดงในตัวอย่างข้างต้น)

mixin นี้จะนับแต่ละแสดงผลบนต่อระดับพื้นฐาน ตัวนับเป็นแอตทริบิวต์คลาสดังนั้นแต่ละคลาสจะมีของตัวเอง (โปรแกรมของคุณอาจล้มเหลวหากคุณได้รับคลาสใหม่จากคลาส Person เนื่องจากตัวนับ@number_of_displaysสำหรับคลาสที่ได้รับจะไม่ถูกกำหนดค่าเริ่มต้น) คุณอาจต้องการแทนที่@number_of_displaysด้วย@@ number_of_displaysเพื่อให้เป็นตัวนับทั่วโลก ในกรณีนี้ลำดับชั้นแต่ละชั้นจะมีตัวนับของตัวเอง หากคุณต้องการตัวนับส่วนกลางและไม่ซ้ำใครคุณควรกำหนดให้เป็นแอตทริบิวต์โมดูล

ทั้งหมดนี้ไม่ง่ายเลยสำหรับฉันเมื่อฉันเริ่มต้นกับ Ruby

ฉันยังไม่สามารถหาวิธีทำให้วิธีการมิกซ์อินเหล่านี้เป็นแบบส่วนตัวหรือป้องกันได้อย่างหมดจด ( ควรรวมเฉพาะวิธีการแสดงผลและnumber_of_displaysเป็นวิธีสาธารณะ)


7

ให้ความสนใจกับสัญกรณ์ช่วง

(อย่างน้อยก็ให้ความสนใจมากกว่าที่ฉันทำตอนแรก!)

มีความแตกต่างระหว่าง0..10 (สองจุด) และ0...10(สามจุด)

ฉันชอบ Ruby มาก แต่สิ่งที่เป็นจุดกับจุดจุดจุดนี้ทำให้ฉันมีปัญหา ฉันคิดว่าคุณลักษณะ "ไวยากรณ์คู่" ที่ละเอียดอ่อนเช่นนี้คือ:

  • พิมพ์ผิดง่ายและ
  • พลาดง่ายด้วยสายตาของคุณในขณะที่มองดูรหัส

ไม่ควรทำให้เกิดข้อบกพร่องแบบ off-by-one ในโปรแกรมของฉัน


1
ไม่แตกต่างจากfor (i=0; i<max; i++)และfor (i=0; i<=max; i++)
ก.

ฉันพยายามค้นหาว่าอะไรคือความแตกต่างระหว่าง 0..10 และ 0 ... 10
Luis D Urraca

6

ฉันคิดว่า " and" และ " or" เป็นการพยักหน้าให้ Perl ซึ่งเป็นหนึ่งใน "พ่อแม่" ที่ชัดเจนกว่าของ Ruby (คนอื่น ๆ ที่โดดเด่นที่สุดคือ Smalltalk) ทั้งสองมีลำดับความสำคัญต่ำกว่ามาก (ในความเป็นจริงต่ำกว่าการมอบหมายซึ่งเป็นที่มาของพฤติกรรมที่ระบุไว้) มากกว่า&&และ||ตัวดำเนินการใดที่คุณควรใช้

สิ่งอื่น ๆ ที่ควรระวังที่ไม่ชัดเจนในทันที:

คุณไม่เรียกเมธอด / ฟังก์ชันจริงๆแม้ว่ามันจะดูเป็นอย่างนั้นก็ตาม เช่นเดียวกับใน Smalltalk คุณส่งข้อความไปยังวัตถุ ดังนั้นเป็นจริงมากขึ้นเช่นmethod_missingmessage_not_understood

some_object.do_something(args)

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

some_object.send(:do_something, args) # note the :

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

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


ขอบคุณ. มีสิ่งหนึ่งที่ฉันเกลียดเกี่ยวกับวิธีการส่ง : ช่วยให้คุณสามารถเรียกใช้วิธีการส่วนตัวได้แม้อยู่นอกชั้นเรียน! อุ๊ย.
MiniQuark

1
@MiniQuark: นั่นคือสิ่งที่ฉันชอบเกี่ยวกับวิธีการส่ง!
Andrew Grimm

6

หากคุณประกาศ setter (aka mutator) โดยใช้attr_writerหรือattr_accessor(หรือdef foo=) โปรดระวังการเรียกจากในคลาส นับตั้งแต่มีการประกาศตัวแปรโดยปริยายล่ามมักจะมีการแก้ไขfoo = barเป็นประกาศตัวแปรใหม่ที่ชื่อว่า foo self.foo=(bar)แทนที่จะเรียกวิธีการ

class Thing
  attr_accessor :foo
  def initialize
    @foo = 1      # this sets @foo to 1
    self.foo = 2  # this sets @foo to 2
    foo = 3       # this does *not* set @foo
  end
end

puts Thing.new.foo #=> 2

นอกจากนี้ยังใช้กับวัตถุ Rails ActiveRecord ซึ่งได้รับตัวเข้าถึงที่กำหนดตามฟิลด์ในฐานข้อมูล เนื่องจากพวกเขาไม่ได้แม้กระทั่ง @ ตัวแปรเช่นสไตล์, วิธีการที่เหมาะสมในการตั้งค่าเหล่านั้นเป็นรายบุคคลอยู่กับหรือself.value = 123self['value'] = 123


5

การทำความเข้าใจความแตกต่างระหว่างคลาสเวลาและวันที่ ทั้งสองอย่างแตกต่างกันและได้สร้างปัญหาในขณะที่ใช้ในราง บางครั้งคลาสเวลาขัดแย้งกับไลบรารีคลาสเวลาอื่น ๆ ที่มีอยู่ในไลบรารีทับทิม / รางมาตรฐาน โดยส่วนตัวแล้วฉันใช้เวลามากในการทำความเข้าใจว่าเกิดอะไรขึ้นในแอปรางของฉัน ต่อมาฉันคิดว่าเมื่อฉันทำ

Time.new

มันหมายถึงห้องสมุดบางแห่งในตำแหน่งที่ฉันไม่รู้ด้วยซ้ำ

ขออภัยหากฉันไม่ชัดเจนกับสิ่งที่ฉันต้องการจะพูดอย่างตรงไปตรงมา หากผู้อื่นประสบปัญหาที่คล้ายกันโปรดอธิบายอีกครั้ง


4

สิ่งที่ทำให้ฉันนึกถึงในอดีตก็คืออักขระขึ้นบรรทัดใหม่ ( \n) ลำดับการหลีกระหว่างอื่น ๆ - ไม่รองรับสตริงภายในเครื่องหมายคำพูดเดียว แบ็กสแลชเองก็หนีไป คุณต้องใช้เครื่องหมายคำพูดคู่เพื่อให้การ Escape ทำงานตามที่คาดไว้


1
และที่แตกต่างจากภาษาอื่น ๆ ?
Robert Gamble

Java สำหรับหนึ่ง เครื่องหมายคำพูดเดี่ยวใน Java สามารถใช้เพื่อใส่ถ่านตัวเดียวเท่านั้นไม่ใช่ Strings
John Topley

1
สิ่งนี้สอดคล้องกับภาษาใด ๆ ที่ให้คุณใช้เครื่องหมายคำพูดเดี่ยวสำหรับสตริงและเป็นเหตุผลว่าทำไมจึงเป็นเช่นนั้น
singpolyma

@ จอห์น: จริง แต่ '\ n' ใน Java จะยังคงเป็นอักขระขึ้นบรรทัดใหม่
Jorn

1
แต่ในคำพูดเดี่ยวของ Java จะสร้างค่าของชนิดถ่านเท่านั้น ไม่ใช่สตริง นั่นคือความแตกต่าง
jmucchiello

4
x = (true and false) # x is false

0 และ '' เป็นจริงตามที่คุณระบุ

คุณสามารถมีเมธอดและโมดูล / คลาสโดยใช้ชื่อเดียวกันได้ (ซึ่งสมเหตุสมผลเพราะเมธอดนั้นถูกเพิ่มลงในอ็อบเจ็กต์ดังนั้นจึงมีเนมสเปซของตัวเอง)

ไม่มีการสืบทอดหลายรายการ แต่มักใช้ "โมดูลมิกซ์อิน" เพื่อเพิ่มวิธีการทั่วไปให้กับหลายคลาส


0 == true // อ๊ะคอมไพเลอร์ c ในสมองฉันระเบิด !!
kenny

1
0 == true ให้เท็จใน Ruby 0 นั้นสมเหตุสมผลเพราะ true เป็นอ็อบเจกต์ใน Ruby ใน C 0 มีการแทนค่าเดียวกับเท็จ
Jules

ในเงื่อนไข Ruby เท่านั้นfalseและnilเป็นเท็จ อื่น ๆ ทั้งหมดคือคุณค่าที่แท้จริง
Rubyprince

4

วิธีการสามารถกำหนดใหม่ได้และสามารถกลายเป็นเครื่องขูดขีดจนกว่าคุณจะค้นพบสาเหตุ ( เป็นที่ยอมรับข้อผิดพลาดนี้อาจ "ยากกว่า" เล็กน้อยในการตรวจจับเมื่อการกระทำของตัวควบคุม Ruby on Rails ถูกกำหนดใหม่โดยไม่ได้ตั้งใจ! )

#demo.rb
class Demo

  def hello1
    p "Hello from first definition"
  end

  # ...lots of code here...
  # and you forget that you have already defined hello1

  def hello1
    p "Hello from second definition"
  end

end
Demo.new.hello1

วิ่ง:

$ ruby demo.rb
=> "Hello from second definition"

แต่เรียกมันโดยเปิดใช้คำเตือนและคุณจะเห็นเหตุผล:

$ ruby -w demo.rb
demo.rb:10: warning: method redefined; discarding old hello1
=> "Hello from second definition"

ฉันจะ +100 การใช้คำเตือนถ้าทำได้
Andrew Grimm

3

ฉันคิดว่ามันเป็นสิ่งที่ดีที่จะใช้. length กับสิ่งต่าง ๆ ... เนื่องจากขนาดได้รับการสนับสนุนจากเกือบทุกอย่างและ Ruby มีประเภทไดนามิกที่คุณจะได้รับผลลัพธ์ที่แปลกจริงๆเรียกขนาดเมื่อคุณมีประเภทผิด ... ฉันค่อนข้างจะได้รับ NoMethodError: วิธีการที่ไม่ได้กำหนด `` ความยาว '' ดังนั้นฉันจึงไม่เคยเรียกขนาดของวัตถุใน Ruby

กัดฉันมากกว่าหนึ่งครั้ง

จำไว้ว่าวัตถุมีรหัสด้วยดังนั้นฉันจึงพยายามไม่ใช้ตัวแปรเรียก id หรือ object_id เพื่อหลีกเลี่ยงความสับสน ถ้าฉันต้องการ id ในอ็อบเจ็กต์ Users ควรเรียกมันว่า user_id

แค่สองเซ็นต์ของฉัน


2

ฉันยังใหม่กับทับทิมและในรอบแรกของฉันฉันประสบปัญหาเกี่ยวกับการเปลี่ยนลอย / สตริงเป็นจำนวนเต็ม ฉันเริ่มต้นด้วยลอยและรหัสทุกอย่างตามที่f.to_int แต่เมื่อฉันดำเนินการต่อและใช้วิธีการเดียวกันกับสตริงฉันก็ถูกโยนเส้นโค้งเมื่อมันมาถึงการรันโปรแกรม

เห็นได้ชัดว่าสตริงไม่มีเมธอดto_intแต่จะลอยและ ints

irb(main):003:0* str_val = '5.0'
=> "5.0"
irb(main):006:0> str_val.to_int
NoMethodError: undefined method `to_int' for "5.0":String
        from (irb):6
irb(main):005:0* str_val.to_i
=> 5


irb(main):007:0> float_val = 5.0
=> 5.0
irb(main):008:0> float_val.to_int
=> 5
irb(main):009:0> float_val.to_i
=> 5
irb(main):010:0>

วงเล็บโดยพลการโยนฉันในตอนแรกด้วย ฉันเห็นรหัสบางรหัสและบางรหัสไม่มี ฉันใช้เวลาสักพักกว่าจะรู้ว่ารูปแบบใดรูปแบบหนึ่งเป็นที่ยอมรับ


2

เกี่ยวข้องกับการตอบสนองของมงกุฏทับทิม to_fooวิธีการพวกเขาจะเปลี่ยนใจเลื่อมใสอย่างเข้มงวดเพียงใด

คนที่ชอบสั้นto_i, to_sบอกว่ามันจะขี้เกียจและแปลงให้เป็นประเภทเป้าหมายแม้ว่าพวกเขาจะไม่สามารถที่จะเป็นตัวแทนอย่างถูกต้องในรูปแบบที่ ตัวอย่างเช่น:

"10".to_i == 10
:foo.to_s == "foo"

ฟังก์ชั่นอีกต่อไปอย่างชัดเจนชอบto_int, to_sหมายถึงว่าวัตถุที่สามารถแสดงกำเนิดเป็นชนิดของข้อมูลที่ ยกตัวอย่างเช่นRationalระดับหมายถึงตัวเลขเหตุผลทั้งหมดเพื่อที่จะสามารถเป็นตัวแทนโดยตรงเป็น Fixnum (หรือ Bignum) to_intจำนวนเต็มโดยการเรียก

Rational(20,4).to_int == 5

หากคุณไม่สามารถเรียกเมธอดที่ยาวกว่าได้นั่นหมายความว่าอ็อบเจ็กต์นั้นไม่สามารถแสดงในประเภทนั้นได้

โดยพื้นฐานแล้วเมื่อทำการแปลงหากคุณขี้เกียจกับชื่อเมธอด Ruby จะขี้เกียจกับการแปลง


1
"ขี้เกียจ" เป็นคำที่ถูกต้องหรือไม่?
Andrew Grimm


1

การทำซ้ำมากกว่าแฮชทับทิมไม่รับประกันว่าจะเกิดขึ้นตามลำดับใด ๆ (ไม่ใช่บั๊ก แต่เป็นคุณสมบัติ)

Hash#sort มีประโยชน์หากคุณต้องการคำสั่งซื้อเฉพาะ

คำถามที่เกี่ยวข้อง: ทำไมอาร์เรย์ของคีย์และค่าของแฮช 1,000 คู่ของรูบี้จึงเรียงลำดับกันเสมอ?


4
สิ่งนี้ไม่ถูกต้องสำหรับ 1.9: "ใน Ruby 1.9 อย่างไรก็ตามองค์ประกอบแฮชจะวนซ้ำตามลำดับการแทรก" จากภาษาโปรแกรม Ruby
Özgür

0

สิ่งนี้ทำให้ฉันโกรธครั้งหนึ่ง:

1/2 == 0.5 #=> false
1/2 == 0   #=> true

ฉันเชื่อว่าสิ่งนี้จะทำงานในลักษณะเดียวกันใน Java, C และ C ++
Larry

เป็นเรื่องตลกฉันไม่ได้คิดเกี่ยวกับเรื่องนี้ แต่ถ้าคุณเปิด irb และลองสิ่งนี้มันก็สมเหตุสมผล: ดังนั้น (1/2) คือ Fixnum และ (0.5) คือ Float และเรารู้ว่า Fixnim! = Float
DemitryT

2
@DemitryT ฉันคิดว่าเหตุผลที่ง่ายกว่าคือการ1/2ประเมินเป็น0ซึ่งไม่เท่ากัน0.5โดยไม่คำนึงถึงประเภท อย่างไรก็ตามRational(1, 2) == 0.5และ1.0 == 1.
Max Nanasy

สะอึกภาษาสากลที่นี่ นี่คือสิ่งที่คนใหม่สำหรับทับทิมและการเขียนโปรแกรมควรรู้
dtc

0
1..5.each {|x| puts x}

ไม่ทำงาน คุณต้องใส่ช่วงไว้ในวงเล็บเช่น

(1..5).each {|x| puts x}

จึงไม่คิดว่าคุณจะโทร5.eachมา ฉันคิดว่านี่เป็นประเด็นสำคัญเช่นเดียวกับx = true and falsegotcha


ฉันจะเรียกมันว่าวงเล็บแทน ประการที่สองหากโค้ดใด ๆ ดูเหมือนว่าจะมีปัญหาเรื่องค่าส่งคืน / ลำดับความสำคัญก็ควรอยู่ในวงเล็บอยู่ดี ดังนั้นสำหรับฉันแล้วไม่มีอะไรพิเศษสำหรับ "gotcha" นี้ คุณสามารถเขียน "gotchas" เชิงผสมได้ทุกครั้งซึ่งจะเป็นการเสียเวลา เพื่อนที่ตรงไปตรงมาแม้ว่าคุณจะได้รับผลลัพธ์ที่คาดหวังจากสิ่งนี้ฉันก็ยังคงชอบล้อมรอบด้วยวงเล็บ
Özgür
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.