มือใหม่พึ่งเคยลองใช้ ruby อยากถามอะไรหน่อยครับ... ไอ้เครื่องหมาย : ข้างหน้าตัวแปรนี่มีไว้ทำอะไรเหรอครับ...

พอดีเวลาทำ accessor เห็นมี

attr_accessor :one,:two,:three แต่เวลาสร้าง method one two three ไม่เห็นจะต้องมี : อยู่หน้าครับ..

แล้วก็เวลาสร้าง map

{:a => 2, :b => 4 }

อีกนิดนึง..

render :text =>”blahblah”

ผมเลยงงๆนิดหน่อยว่าแล้ว : มีไว้ทำอะไรเหรอครับ

(จะว่าไปแล้วตัว => นี่หมายถึงอะไรน่ะครับ….)

ขอบคุณมากครับ

apirak's picture

เวลาใช้ {:a => 2, :b => 4} เราสามารถใช้ {‘a’ => 2, ‘b’ => 4} ได้นะครับได้ผลเหมือนกัน เพราะมันรับ string เหมือนกัน แต่การใช้ : เพื่อแสดงความเป็น symbol ครับ

ทดลองแทน attr_accessor :one,:two,:three ด้วย attr_accessor ‘one’, ‘two’, ‘three’ จะได้ผลเหมือนกัน

คำถามเรื่อง : ลองอ่านเพิ่มเติมใน สนุกกับภาษา ruby ตอน Symbols นะครับ

ส่วนคำถามเรื่องเครื่องหมาย => ให้ดูใน สนุกกับภาษา ruby ตอน Hashes

และคำถามเรื่อง attr_accessor ลองอ่านบท method นะครับเขียนไว้ละ แต่ยังไม่ได้ post :p

ไม่เหมือนกันนะครับ

อันแรก index เป็น Symbol อันที่สอง index เป็น String เป็นคนละ class กันครับ

irb(main):007:0> (:a).class
=> Symbol
irb(main):008:0> ('a').class
=> String
irb(main):009:0> (:a)==('a')
=> false
irb(main):010:0> (:a).to_s==('a')
=> true

ทดลองดูนะครับ

irb(main):001:0> a = {'a' => 10, 'b' => 20}
=> {"a"=>10, "b"=>20}
irb(main):002:0> a[:a]
=> nil
irb(main):003:0> a['a']
=> 10
irb(main):005:0> a[:a] = 200
=> 200
irb(main):006:0> a
=> {"a"=>10, "b"=>20, :a=>200}

Symbol มีลักษณะคล้าย String ที่ไม่สามารถแก้ได้ (เหมือนกับที่พี่ป๊อกบอกครับ)

apirak's picture

คุณ jittat ช่วยจุดประกายผมอีกคน :)

พอเห็นบรรทัดนี้ (:a) == ('a') #=> false เลยสงสัยว่าแล้วแบบนี้เวลาที่เรา ต้องการเขียน code ที่ต้องการรับได้ทั้ง String และ Symbol เราจะทำอย่างไรดี

ผมเลยลองแกะ code แบบพี่ป๊อก(pphetra)

ผมลองเข้าไปดูคำสั่ง find

     # File vendor/rails/activeresource/lib/active_resource/base.rb, line 381
381:       def find(*arguments)
382:         scope   = arguments.slice!(0)
383:         options = arguments.slice!(0) || {}
384: 
385:         case scope
386:           when :all   then find_every(options)
387:           when :first then find_every(options).first
388:           when :one   then find_one(options)
389:           else             find_single(scope, options)
390:         end
391:       end

ผมคิดว่าคำเฉลยอยู่ใน scope = arguments.slice!(0)

เลยตามไปดู slice! ต่อ

    # File vendor/rails/activesupport/lib/active_support/core_ext/hash/slice.rb, line 16
16:         def slice(*keys)
17:           allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
18:           reject { |key,| !allowed.include?(key) }
19:         end
 
22:         def slice!(*keys)
23:           replace(slice(*keys))
24:         end

คำสั่ง slice! มันเรียก slice อีกที ที่นี้ลองแกะ if ออกมาให้ดูง่ายๆ

allowed = 
 
Set.new(
respond_to?(:convert_key) ? 
  keys.map { |key| 
    convert_key(key) 
  } 
:
  keys
)

ยังไม่รู้อีกหลายคำสั่ง แต่ผมว่าคำตอบของเราอยู่ใน convert_key(key) นี่ล่ะ

    # File vendor/rails/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb, line 74
74:     def convert_key(key)
75:       key.kind_of?(Symbol) ? key.to_s : key
76:     end

พอมาเจอตัวนี้เลยงงไปเลยครับ ตอนแรกคิดว่าถ้ากรณีที่ไม่ใช้ String มันจะ convert ไปเป็น symbol ซะอีก กลายเป็น key.to_s

ผมเลยหยุดความพยายามไว้แค่นี้ก่อนที่จะเลยเถิดไป… แต่ได้ข้อสรุปเล็กน้อยว่าน่าจะเลือกแกะ method อื่นที่มันยอมให้ใช้ทั้ง symbol และ string มากกว่า (ไง่อยู่นาน) สำหรับตอนนี้คิดว่า ถ้าต้องการให้ method ของเราทำงานได้ทั้ง string และ symbol ก็แปลงมันเป็น string ให้หมดซะเลย

apirak's picture

หาข้อมูลเพิ่มเติมอีกหน่อย

ใน rails ตอนที่เราดู log จะเห็นว่าค่า parameter เขียน log ออกมาแบบนี้

Parameters: {“action” => “edit”, “id” => “1”, “controller” => “posts”}

ตอนแรกเข้าใจว่ามันเป็น Hash ธรรมดา

เวลาเราเรียกค่ามาดูจะใช้ params[“action”] ได้ค่าเป็น “edit” ปัญหาอยู่ที่เราสามารถเรียกแบบนีได้ด้วย params[:action] ได้ค่าเป็น “edit” เหมือนกัน ซะงั้น

หลังจากกระทู้นี้เลยไปหาความจริงมา ปรากฏว่า params มันไม่ใช่ Hash อย่างที่คิดแต่มันเป็น HashWithIndifferentAccess เพื่อให้เราสามารถเรียก value ได้ทั้ง ‘a’ และ :a

ลองเข้าไปดูใน code พบว่ามันใช้คำสั่ง convert_key ทำให้ key กลายเป็น string ซะก่อน

ขอบคุณ คุณ jittat และพี่ป๋อกหลายๆ นะครับ ที่ช่วยจุดประกาย ^_^* ให้เริ่มแกะ code

ดูคุณ apirak แกะก็สนุกเหมือนกันนะครับ

ถ้ามันแปลง key เป็น string อย่างนี้ แสดงว่าความตั้งใจในการใช้ symbol ก็เพื่อ ให้โปรแกรมอ่านง่าย เป็นหลัก

ผมว่าเพราะว่ามันดันให้ใส่ได้สองอย่าง แต่ String ทั้งหมดอาจแปลงเป็น Symbol ไม่ได้ เลยเอากลับทางกันแทนมั้งครับ

เห็นงี้รู้สึกได้ว่าโปรแกรมจะรันช้าแน่ ๆ เลย

Symbol ถือเป็น object พิเศษแบบหนึ่ง นั่นคือ มันจะมีแค่ตัวเดียวเสมอ (ถ้าชื่อเหมือนกัน)
สมมติเราอ้างถึง :a ไป 10 ครั้งใน program
ก็จะมี object symbol :a เกิดขึ้นใน memory แค่ตัวเดียว
แต่ถ้าเราใช้ string 'a' 10 ครั้งใน program
ก็จะมี object string 'a' เกิดขึ้น 10 ตัวใน memory

อ่า …. ในที่สุดก็เข้าใจ มันก็คือ singleton แบบหนึ่งนั่นเอง

ตอนแรกอ่านหนังสือ ผมก็เข้าใจเหมือนคุณ apirak ครับ ว่า symbol กับ string มันคือตัวเดียวกัน จริง ๆ มันซับซ้อนกว่านั้น

ขอบคุณทุกคนมากครับ

^ ขอบคุณมากครับ

เพิ่งโดนกับตัวเองครับ คิดว่าใช้ :x แทน ‘x’ เพื่อให้อ่านง่าย และลดการกดแป้น 1 ครั้ง แล้วผมก็พยายามใช้มาโดยตลอด วันนี้โปรแกรมผมเกิดบั๊ก เนื่องจากไปเปลี่ยน hash[‘x’] เป็น hash[:x] แหม ก็เห็น params[] ยังทำได้ นึกว่า hash จะทำได้บ้าง

เรื่องง่ายๆ ที่ผู้ชายไม่รู้
ขอบคุณทุกๆ ท่าน สำหรับการแลกเปลี่ยนเรียนรู้ครับ

ย้าย Codenone

ประกาศย้าย Codenone ไปใช้ Forum ของ Blognone แทนครับ ตามไปตั้งกระทู้ต่อได้ที่ Codenone Forum (รายละเอียดอ่านจากกระทู้ ย้าย Codenone ไปรวมกับ Blognone)

กระทู้เก่าๆ จะย้ายตามไปในภายหลัง ตอนนี้ปิดการโพสต์กระทู้ไว้ เหลือไว้เฉพาะอ้างอิงเท่านั้น