2016-03-17 19 views
47

tôi đã rối tung xung quanh với fix và sau khi đùa giỡn với nó Tôi đã xem qua một số hành vi kỳ lạ, cụ thể là 0 * undefined*** Exception: Prelude.undefinedundefined * 00. Điều này cũng có nghĩa là fix (0 *)*** Exception: <<loop>>fix (* 0)0.Tại sao nhân chỉ ngắn mạch ở một bên

Sau khi chơi với nó, có vẻ như lý do là vì nó không tầm thường để làm cho nó ngắn mạch theo cả hai hướng, vì điều đó không thực sự có ý nghĩa nhiều, mà không có một số tính toán song song lạ và bắt đầu với đầu tiên không dưới cùng được trả về.

Đây có phải là loại điều được thấy ở những nơi khác (chức năng phản xạ không phản xạ cho giá trị dưới cùng), và đó có phải là điều tôi có thể dựa vào một cách an toàn không? Ngoài ra, có một cách thực tế để làm cho cả hai số (0 *)(* 0) đánh giá bằng 0 bất kể giá trị được chuyển đến.

+0

gÌ? Thật tuyệt vời! – PyRulez

+4

Đây là * chính xác * điểm làm cho [ngữ nghĩa biểu thị] (https://en.wikipedia.org/wiki/Denotational_semantics) không hoàn toàn tương đương với [ngữ nghĩa hoạt động] (https://en.wikipedia.org/wiki/ Operational_semantics), tức là không hoàn toàn trừu tượng. Cái trước có thể biểu diễn một hàm 'f' với' f undefined 0 = 0' và 'f 0 undefined = 0' trong khi hàm thứ hai không thể. Việc triển khai ngôn ngữ tuân theo ngữ nghĩa hoạt động, do đó làm cho nó không thể định nghĩa một 'f' mà không có một số thủ thuật. – Bakuriu

Trả lời

41

Lý do của bạn là chính xác. Có một gói unamb cung cấp các công cụ cho loại tính toán song song mà bạn tham chiếu. Trong thực tế, nó cung cấp Data.Unamb.pmult, cố gắng, song song, để kiểm tra xem mỗi toán hạng là 1 hay 0, và nếu như vậy ngay lập tức tạo ra một kết quả. Cách tiếp cận song song này có khả năng chậm hơn nhiều trong hầu hết các trường hợp cho phép tính số học đơn giản!

Việc đoản mạch (*) xảy ra chỉ trong phiên bản GHC 7.10. Nó đến như là kết quả của những thay đổi để thực hiện loại Integer trong phiên bản GHC đó. Sự lười biếng thêm này thường được xem là lỗi hiệu suất (vì nó cản trở việc phân tích nghiêm ngặt và thậm chí có thể dẫn đến rò rỉ không gian theo lý thuyết), vì vậy nó sẽ bị xóa trong GHC 8.0.

+3

Có vẻ như 7,10 [có một số tính năng "đáng ngạc nhiên"] (http://stackoverflow.com/q/35941674/1139697). :/ – Zeta

+0

Bạn có thể giải thích phần lỗi hiệu năng không. Chắc chắn nếu bạn BIẾT một trong các giá trị là 0, nó cũng không có ý nghĩa gì từ quan điểm hiệu năng để đánh giá nó. Bạn không thể ném nó vào vực thẳm tại thời điểm đó? – semicolon

+0

@semicolon, GHC sử dụng phân tích nghiêm ngặt (cụ thể là phiên bản mà nó gọi là phân tích nhu cầu) để tìm ra khi nào giá trị sẽ * luôn * hoặc * không bao giờ * được đánh giá. Việc biết một trong hai cách này rất hữu ích để tối ưu hóa. Đôi khi (ví dụ: với người dùng '&&') mong đợi và dựa vào hành vi ngắn mạch. Trong nhiều tình huống khác, tiền tiết kiệm trong những trường hợp bất thường không bắt đầu trả cho những tổn thất ở những tình huống khác. – dfeuer

7

Lấy ví dụ sau

(if expensiveTest1 then 0 else 2) * (if expensiveTest2 then 0 else 2) 

Bạn phải chọn một bên để đánh giá. Nếu expensiveTest2 là vòng lặp vô hạn, bạn sẽ không bao giờ có thể biết được phía bên phải có phải là 0 hay không, vì vậy bạn không thể biết có hay không đoản mạch bên phải, vì vậy bạn không bao giờ nhìn vào bên trái. Bạn không thể kiểm tra xem cả hai bên là 0 cùng một lúc.

Đối với việc bạn có thể dựa vào ngắn mạch để hành động theo một cách nào đó, chỉ cần ghi nhớ rằng undefinederror hành vi chính xác giống như một vòng lặp vô hạn miễn là bạn không sử dụng IO. Do đó, bạn có thể thử nghiệm đoản mạch và lười biếng bằng cách sử dụng undefinederror. Nói chung, hành vi ngắn mạch thay đổi theo chức năng. (Cũng có các mức độ lười biếng khác nhau. undefinedJust undefined có thể cho kết quả khác nhau.)

Xem this để biết thêm chi tiết.

6

Trên thực tế, có vẻ như fix (* 0) == 0 chỉ hoạt động cho Integer, nếu bạn chạy fix (* 0) :: Double hoặc fix (* 0) :: Int, bạn vẫn nhận được ***Exception <<loop>>

Đó là bởi vì trong instance Num Integer, (*) được định nghĩa là (*) = timesInteger

timesInteger được định nghĩa trong Data.Integer

-- | Multiply two 'Integer's 
timesInteger :: Integer -> Integer -> Integer 
timesInteger _  (S# 0#) = S# 0# 
timesInteger (S# 0#) _  = S# 0# 
timesInteger x  (S# 1#) = x 
timesInteger (S# 1#) y  = y 
timesInteger x  (S# -1#) = negateInteger x 
timesInteger (S# -1#) y  = negateInteger y 
timesInteger (S# x#) (S# y#) 
    = case mulIntMayOflo# x# y# of 
    0# -> S# (x# *# y#) 
    _ -> timesInt2Integer x# y# 
timesInteger [email protected](S# _) y  = timesInteger y x 
-- no S# as first arg from here on 
timesInteger (Jp# x) (Jp# y) = Jp# (timesBigNat x y) 
timesInteger (Jp# x) (Jn# y) = Jn# (timesBigNat x y) 
timesInteger (Jp# x) (S# y#) 
    | isTrue# (y# >=# 0#) = Jp# (timesBigNatWord x (int2Word# y#)) 
    | True  = Jn# (timesBigNatWord x (int2Word# (negateInt# y#))) 
timesInteger (Jn# x) (Jn# y) = Jp# (timesBigNat x y) 
timesInteger (Jn# x) (Jp# y) = Jn# (timesBigNat x y) 
timesInteger (Jn# x) (S# y#) 
    | isTrue# (y# >=# 0#) = Jn# (timesBigNatWord x (int2Word# y#)) 
    | True  = Jp# (timesBigNatWord x (int2Word# (negateInt# y#))) 

Nhìn vào con cá tuyết trên e, nếu bạn chạy (* 0) x, sau đó timesInteger _ (S# 0#) sẽ phù hợp để x sẽ không được đánh giá, trong khi nếu bạn chạy (0 *) x, sau đó khi kiểm tra xem timesInteger _ (S# 0#) trận đấu, x sẽ được đánh giá và gây ra vòng lặp vô hạn

Chúng ta có thể sử dụng dưới mã để kiểm tra nó:

module Test where 
import Data.Function(fix) 

-- fix (0 ~*) == 0 
-- fix (~* 0) == ***Exception<<loop>> 
(~*) :: (Num a, Eq a) => a -> a -> a 
0 ~* _ = 0 
_ ~* 0 = 0 
x ~* y = x ~* y 

-- fix (0 *~) == ***Exception<<loop>> 
-- fix (*~ 0) == 0 
(*~) :: (Num a, Eq a) => a -> a -> a 
_ *~ 0 = 0 
0 *~ _ = 0 
x *~ y = x *~ y 

có một cái gì đó thậm chí thú vị hơn, trong GHCI:

*Test> let x = fix (* 0) 
*Test> x 
0 
*Test> x :: Double 
*** Exception: <<loop>> 
*Test> 
+0

Eh, mà cuối cùng chỉ là sự kết hợp của 'NoMonomorphismRestriction' với mặc định. Phần còn lại của câu trả lời của bạn là công việc thám tử tuyệt vời. – dfeuer

+0

@dfeuer Trong khi bạn nói đúng là nó có ý nghĩa hoàn hảo, tôi vẫn nghĩ nó khá thú vị. Nếu bạn là một người thực sự xấu, bạn có thể trả lại bất kỳ tùy ý 'Num a => a' nhưng làm cho nó thất bại khi được sử dụng như bất cứ điều gì khác hơn là một' Integer'. Chỉ cần thực hiện 'fix (* 0) + retVal'. – semicolon

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