2012-10-16 37 views
8

Nếu các nhà điều hành đường ống được tạo ra như thế này:Tại sao nhà điều hành đường ống hoạt động?

let (|>) f g = g f 

Và sử dụng như thế này:

let result = [2;4;6] |> List.map (fun x -> x * x * x) 

Sau đó, những gì nó có vẻ làm là lấy List.Map và đặt nó đằng sau (vui vẻ x -> x * x * x) Và không thay đổi bất cứ điều gì về vị trí của [2; 4; 6]

Vì vậy, bây giờ nó trông như thế này:

let result2 = [2;4;6] (fun x -> x * x * x) List.map 

Tuy nhiên điều này không hiệu quả.

Tôi chỉ đang học f # lần đầu tiên ngay bây giờ. Và điều này làm phiền tôi khi đọc một cuốn sách về f #. Vì vậy, tôi có thể tìm hiểu những gì tôi đang thiếu sau đó, nhưng tôi quyết định hỏi anyway.

Rõ ràng là tôi thiếu thứ gì đó quan trọng. Vì tôi có thể dễ dàng tạo lại nhà điều hành đường ống. Nhưng tôi không hiểu tại sao nó lại hoạt động. Tôi có thể xấu hổ bản thân mình ngay sau khi tôi tìm hiểu thêm. Oh well.

+1

nhà điều hành không phải là tiền tố không phải là tiền tố - bạn đang áp dụng nó cho những điều sai trái –

Trả lời

11

Nhà điều hành đường ống đơn giản là cú pháp đường cho các cuộc gọi phương thức xích. Nó rất giống với cách biểu thức linq được thể hiện trong C#.

Giải thích từ here:

Forward ống hành Tôi yêu anh chàng này. Các nhà điều hành đường ống Chuyển tiếp đơn giản được định nghĩa là:

let (|>) x f = f x 

Và có một loại chữ ký:

'a -> ('a -> 'b) -> 'b 

Những dịch để: cung cấp một loại chung 'một, và một chức năng mà phải mất một' a và lợi nhuận a 'b, sau đó trả về ứng dụng của hàm trên đầu vào.

Thay vì giải thích này, hãy để tôi cung cấp cho bạn một ví dụ về nơi nó có thể được sử dụng:

// Take a number, square it, then convert it to a string, then reverse that string 
let square x   = x * x 
let toStr (x : int) = x.ToString() 
let rev (x : string) = new String(Array.rev (x.ToCharArray())) 

// 512 -> 1024 -> "1024" -> "4201" 
let result = rev (toStr (square 512)) 

Mã này là rất thẳng về phía trước, nhưng thông báo chỉ cách ngang bướng cú pháp trông. Tất cả những gì chúng ta muốn làm là lấy kết quả của một phép tính và chuyển nó cho phép tính tiếp theo. Chúng tôi có thể viết lại bằng cách giới thiệu một loạt các biến mới:

let step1 = square 512 
let step2 = toStr step1 
let step3 = rev step2 
let result = step3 

Nhưng bây giờ bạn cần giữ tất cả các biến tạm thời đó một cách thẳng thắn. Điều mà toán tử (|>) thực hiện là lấy một giá trị, và 'chuyển tiếp nó' đến một hàm, về cơ bản cho phép bạn chỉ định tham số của hàm trước khi gọi hàm. Điều này đơn giản hóa đáng kể mã F # bằng cách cho phép bạn kết hợp các hàm ống với nhau, trong đó kết quả của một hàm được truyền vào tiếp theo. Vì vậy, để sử dụng các ví dụ tương tự các mã có thể được viết rõ ràng như:

let result = 512 |> square |> toStr |> rev 

Sửa:

Trong F # những gì bạn đang thực sự làm với một phương pháp gọi đang thực hiện một chức năng và sau đó áp dụng nó để thông số sau, vì vậy trong ví dụ của bạn, nó sẽ là List.map (fun x -> x * x * x) được áp dụng cho [2;4;6].Tất cả những gì mà toán tử đường ống thực hiện là lấy các tham số theo thứ tự ngược lại và sau đó làm ứng dụng đảo ngược chúng trở lại.

chức năng: List.map (fun x -> x * x * x) tham số: [2;4;6]

Chuẩn F # gọi cú pháp: f g

Đảo ngược cú pháp F # gọi: g f

Tiêu chuẩn:

let var = List.map (fun x -> x * x * x) [2;4;6] 

Đảo ngược:

let var = [2;4;6] |> List.map (fun x -> x * x * x) 
+0

Tôi biết đường cú pháp của nó và tôi đã tạo ra nó ngay trong mã ở trên. Bây giờ tôi hỏi tại sao chức năng này hoạt động: let (|>) f g = g f - hoặc cách nó hoạt động khác với tôi hiểu nó như tôi đã viết trong câu hỏi của mình. – user1594138

+0

Cảm ơn bạn đã thêm thông tin mà bạn đã nhập. Có phải 2 hàm này đi vào toán tử chuyển tiếp đường ống ?: List.map (vui x -> x * x * x) - Vì tôi đã hiển thị trong bài đăng của mình, sắp xếp lại 2 hàm đó 't làm việc. – user1594138

+0

Ahh Tôi bắt đầu nhận được nó. Cảm ơn! – user1594138

8

Các dấu ngoặc xung quanh |> có nghĩa là nó là một nhà điều hành ghi rất dụ của bạn có thể được viết

let result = (|>) [2;4;6] (List.map (fun x -> x * x * x)) 

Kể từ |> áp dụng đối số đầu tiên của mình vào thứ hai, đây là tương đương với

let result = (List.map (fun x -> x * x)) [2;4;6] 
+0

Các dấu ngoặc đơn không làm cho nó trở thành một toán tử infix. Thêm dấu ngoặc đơn (parens, thực sự) khi _using_ nó biến nó thành một hàm, trái ngược với toán tử. Theo định nghĩa, chúng được yêu cầu cho cả hai toán tử infix và prefix. – Abel

3

Nếu bạn nhập định nghĩa của bạn là |> vào fsi và nhìn vào chữ ký của nhà điều hành bắt nguồn theo kiểu suy luận bạn sẽ chú ý val (|>) : 'a -> ('a -> 'b) -> 'b, tức là đối số 'a được gán cho hàm ('a -> 'b) sản lượng 'b.

Bây giờ chiếu chữ ký này vào biểu hiện của bạn [2;4;6] |> List.map (fun x -> x * x * x) và bạn sẽ nhận được List.map (fun x -> x * x * x) [2;4;6], nơi mà các đối số là danh sách [2;4;6] và chức năng là partially applied chức năng của một đối số List.map (fun x -> x * x * x).

5

Như những người khác đã nói ở trên, về cơ bản bạn hiểu nhầm kết quả2 sẽ giải quyết. Nó thực sự sẽ giải quyết để

List.map (fun x -> x * x * x) [2;4;6]

List.map hai đối số: một chức năng để áp dụng cho tất cả các yếu tố trong một danh sách và một danh sách. (fun x -> x * x * x) là đối số đầu tiên và [2;4;6] là đối số thứ hai.

Về cơ bản, chỉ cần đặt những gì ở bên trái của |> sau khi kết thúc nội dung bên phải.

+2

Rất vui vì bạn đã rõ ràng hơn về thứ tự đối số, với "... sau khi kết thúc" ... Bất kỳ ai đến F # vào cuối trò chơi đã thấy Elixir's '|>' đầu tiên chắc chắn sẽ bị vấp ngã, vì điều đó áp dụng giá trị ở bên trái làm đối số _first_. – kevlarr

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