2016-11-29 17 views
12

Tôi gặp phải tác dụng phụ lạ lùng này gây ra lỗi hoặc nhầm lẫn. Vì vậy, hãy tưởng tượng rằng đây không phải là một ví dụ tầm thường nhưng một ví dụ về một gotcha có lẽ.Tại sao xẻng kép ở trạng thái không biến đổi của Ruby?

name = "Zorg" 

def say_hello(name) 
    greeting = "Hi there, " << name << "?" 
    puts greeting 
end 

say_hello(name) 
puts name 

# Hi there, Zorg? 
# Zorg 

Điều này không làm thay đổi tên. Tên vẫn là Zorg.

Nhưng bây giờ hãy nhìn vào một sự khác biệt rất tinh tế. trong ví dụ tiếp theo này:

name = "Zorg" 

def say_hello(name) 
    greeting = name << "?" 
    puts "Hi there, #{greeting}" 
end 

say_hello(name) 
puts name 

# Hi there, Zorg? 
# Zorg? <-- name has mutated 

Bây giờ, tên là Zorg?. Khùng. Sự khác biệt rất tinh tế trong nhiệm vụ greeting =. Ruby đang làm một cái gì đó bên trong với phân tích cú pháp (?) Hoặc thông điệp đi qua chuỗi? Tôi nghĩ rằng điều này sẽ chỉ chuỗi xẻng như name.<<("?") cùng nhưng tôi đoán điều này không xảy ra.

Đây là lý do tại sao tôi tránh toán tử xẻng khi cố gắng ghép nối. Tôi thường cố gắng tránh tình trạng đột biến khi tôi có thể nhưng Ruby (hiện tại) không được tối ưu hóa cho điều này (chưa). Có lẽ Ruby 3 sẽ thay đổi mọi thứ. Xin lỗi vì thảo luận về phạm vi-creep/bên về tương lai của Ruby. Tôi nghĩ điều này đặc biệt kỳ lạ vì ví dụ có ít tác dụng phụ hơn (ví dụ đầu tiên) có hai toán tử xẻng trong đó ví dụ có nhiều tác dụng phụ hơn có ít công cụ xẻng hơn.

Cập nhật Bạn đúng DigitalRoss, tôi đang làm cho nó quá phức tạp. Ví dụ bị giảm:

one = "1" 
two = "2" 
three = "3" 
message = one << two << three 

Bây giờ bạn nghĩ mọi thứ được đặt thành? (không nhìn trộm!) Nếu tôi phải đoán tôi muốn nói:

one is 123 
two is 23 
three is 3 
message is 123 

Nhưng tôi đã sai về hai. Hai là 2.

+1

Chỉ cần lưu ý, cách hợp quy để nối chuỗi trong Ruby là nội suy, thay vì nối với chuỗi cộng hoặc xẻng kép. cùng với nhau. Tôi đoán rằng Matz đã chọn để làm cho xẻng đôi liên kết cách nó để hỗ trợ các trường hợp sử dụng "a" << b << "c". Nếu không, mọi người đến từ các ngôn ngữ như C++ sẽ rất ngạc nhiên khi thấy các phản ứng phụ. Re tránh trạng thái biến đổi, Ruby hỗ trợ kiểu dáng chức năng cũng như nhiều kiểu khác; Ruby không phải là ngôn ngữ chức năng thuần túy và tôi nghĩ đó là ý định. – bloopletech

+0

Hoàn toàn. Tôi thường làm "# {one} # {two} # {three}". – squarism

Trả lời

9

Nếu chúng tôi chuyển đổi của bạn a << b << c xây dựng thành một dạng phương thức khác và ném vào một loạt các dấu ngoặc đơn tiềm ẩn, hành vi phải rõ ràng hơn. Viết lại:

greeting = "Hi there, " << name << "?" 

sản lượng:

greeting = ("Hi there, ".<<(name)).<<("?") 

String#<< được sửa đổi thứ nhưng không bao giờ name xuất hiện như là mục tiêu/LHS của <<, chuỗi "Hi there ," << name làm nhưng name không.Nếu bạn thay thế các chuỗi đầu đen với một biến:

hi_there = 'Hi there, ' 
greeting = hi_there << name << '?' 
puts hi_there 

bạn sẽ thấy rằng << thay đổi hi_there; trong trường hợp "Hi there, " của bạn, thay đổi này đã bị ẩn vì bạn đang sửa đổi thứ gì đó (chuỗi ký tự) mà bạn không thể xem xét sau đó.

+0

Đúng. Tốt đẹp. 'one =" 1 "; hai = "2"; ba = "3"; greeting = (một. << (hai. << (ba))) 'làm những gì tôi mong đợi. – squarism

3

Bạn đang làm cho nó quá phức tạp.

Toán tử trả về phía bên tay trái và vì vậy trong trường hợp đầu tiên, nó chỉ đọc name (vì "Hi there, " << name được đánh giá trước) nhưng trong ví dụ thứ hai nó đang viết nó.

Hiện tại, nhiều toán tử Ruby phải liên kết, nhưng << không phải là một trong số chúng. Xem: https://stackoverflow.com/a/21060235/140740

+0

Cảm ơn thuật ngữ bên trái/bên phải mà tôi đã bỏ lỡ. –

1

Phía bên tay phải của = được đánh giá từ trái sang phải.

Khi bạn đang làm

"Hello" << name << "?" 

Các hoạt động bắt đầu với "Hello", thêm name với nó, sau đó thêm "?" đến biến đổi "Hello".

Khi bạn làm

name << "?" 

Các hoạt động bắt đầu với name, và cho biết thêm "?" với nó, biến đổi tên (mà tồn tại bên ngoài phạm vi nội bộ của phương pháp

Vì vậy, trong ví dụ của bạn của one << two << three., Bạn chỉ đang tắt tiếng one

Các vấn đề liên quan