2010-01-29 30 views
24

Tôi vừa bắt đầu đùa giỡn với F # trong Mono và vấn đề sau nảy sinh mà tôi không thể hiểu được. Tìm kiếm thông tin trên printfnTextWriterFormat cũng không mang lại sự giác ngộ, vì vậy tôi nghĩ tôi sẽ hỏi ở đây.Loại printfn trong F #, chuỗi tĩnh và động

Trong FSI tôi chạy như sau:

> "hello";; 
val it : string = "hello" 
> printfn "hello";; 
hello 
val it : unit =() 

Chỉ cần một chuỗi bình thường và in nó. Khỏe. Bây giờ tôi muốn khai báo một biến để chứa rằng cùng một chuỗi và in nó cũng như:

> let v = "hello" in printfn v ;; 
let v = "hello" in printfn v ;; 
---------------------------^ 
\...\stdin(22,28): error FS0001: The type 'string' is not compatible with the type 'Printf.TextWriterFormat<'a>' 

tôi hiểu từ đọc sách của tôi mà printfn đòi hỏi một chuỗi liên tục. Tôi cũng hiểu rằng tôi có thể giải quyết vấn đề này với một số thứ như printfn "%s" v.

Tuy nhiên, tôi muốn hiểu những gì đang xảy ra với việc nhập ở đây. Rõ ràng, "hello" là loại string cũng như v là. Tại sao lại có vấn đề về loại? printfn có gì đặc biệt không? Khi tôi hiểu nó, trình biên dịch đã thực hiện kiểm tra kiểu trên các đối số của chuỗi đầu tiên, chẳng hạn như printfn "%s" 1 không thành công .. điều này tất nhiên không hoạt động với các chuỗi động, nhưng tôi cho rằng đó là một sự thuận tiện đơn giản từ phía trình biên dịch cho trường hợp tĩnh.

Trả lời

25

Câu hỏi hay. Nếu bạn xem loại printfn, là Printf.TextWriterFormat<'a> -> 'a, bạn sẽ thấy trình biên dịch tự động ép buộc chuỗi thành các đối tượng TextWriterFormat tại thời gian biên dịch, suy ra thông số loại thích hợp 'a. Nếu bạn muốn sử dụng printfn với một chuỗi động, bạn chỉ có thể thực hiện chuyển đổi mà bản thân:

let s = Printf.TextWriterFormat<unit>("hello") 
printfn s 

let s' = Printf.TextWriterFormat<int -> unit>("Here's an integer: %i") 
printfn s' 10 

let s'' = Printf.TextWriterFormat<float -> bool -> unit>("Float: %f; Bool: %b") 
printfn s'' 1.0 true 

Nếu chuỗi được tĩnh được biết đến (như trong ví dụ trên), sau đó bạn vẫn có thể có trình biên dịch suy ra bên phải Lập luận tổng quát để TextWriterFormat thay vì gọi các nhà xây dựng:

let (s:Printf.TextWriterFormat<_>) = "hello" 
let (s':Printf.TextWriterFormat<_>) = "Here's an integer: %i" 
let (s'':Printf.TextWriterFormat<_>) = "Float: %f; Bool: %b" 

Nếu chuỗi thực sự năng động (ví dụ như nó đọc từ một tập tin), sau đó bạn sẽ cần phải sử dụng một cách rõ ràng các thông số loại và gọi constructor như tôi đã làm trong các ví dụ trước.

7

Tôi không nghĩ rằng giá trị bằng chữ "hello" là loại String khi được sử dụng trong ngữ cảnh printfn "hello". Trong ngữ cảnh này trình biên dịch sẽ nhập loại giá trị bằng chữ là Printf.TextWriterFormat<unit>. Lúc đầu, có vẻ lạ với tôi rằng một giá trị chuỗi chữ sẽ có một kiểu suy luận khác nhau tùy thuộc vào ngữ cảnh của nó, nhưng tất nhiên chúng ta được sử dụng để xử lý các số nguyên, có thể đại diện cho các số nguyên , số thập phân, phao nổi, v.v., tùy thuộc vào nơi chúng xuất hiện.

Nếu bạn muốn khai báo biến trước khi sử dụng nó qua printfn, bạn có thể khai báo nó với một loại rõ ràng ...

let v = "hello" : Printf.TextWriterFormat<unit> in printfn v 

... hoặc bạn có thể sử dụng các nhà xây dựng cho Printf.TextWriterFormat để chuyển đổi a bình thường Giá trị chuỗi cho loại cần thiết ...

let s = "foo" ;; 
let v = new Printf.TextWriterFormat<unit>(s) in printfn v ;; 
4

Khi quan sát một cách chính xác, hàm printfn mất một "Printf.TextWriterFormat < 'a>" không phải là một chuỗi. Trình biên dịch biết cách chuyển đổi giữa chuỗi không đổi và "Printf.TextWriterFormat < 'a>", nhưng không phải giữa chuỗi động và "Printf.TextWriterFormat <' a>".

Điều này đặt ra câu hỏi tại sao nó không thể chuyển đổi giữa chuỗi động và "Printf.TextWriterFormat < 'a>". Điều này là do trình biên dịch phải xem xét nội dung của chuỗi và xác định các ký tự điều khiển nào trong đó (ví dụ% s% i v.v), từ đây nó hoạt động ra kiểu tham số kiểu của "Printf.TextWriterFormat < ' a> "(tức là 'một chút). Đây là một hàm được trả về bởi hàm printfn và có nghĩa là các tham số khác được printfn chấp nhận hiện được gõ mạnh.

Để làm rõ điều này trong ví dụ "printfn"% s "", "% s" được chuyển đổi thành "Printf.TextWriterFormat unit>", có nghĩa là loại "printfn"% s "" là chuỗi -> đơn vị.

7

Điều này chỉ liên quan đến câu hỏi của bạn, nhưng tôi nghĩ đó là một mẹo tiện dụng. Trong C#, tôi thường có mẫu dây để sử dụng với String.Format lưu trữ như các hằng số, vì nó làm cho mã sạch hơn:

String.Format(SomeConstant, arg1, arg2, arg3) 

Thay vì ...

String.Format("Some {0} really long {1} and distracting template that uglifies my code {2}...", arg1, arg2, arg3) 

Nhưng kể từ khi gia đình printf các phương pháp nhấn mạnh trên chuỗi chữ thay vì giá trị, ban đầu tôi nghĩ rằng tôi không thể sử dụng phương pháp này trong F # nếu tôi muốn sử dụng printf. Nhưng sau đó tôi nhận ra rằng F # có một cái gì đó tốt hơn - một phần ứng dụng chức năng.

let formatFunction = sprintf "Some %s really long %i template %i" 

Điều đó vừa tạo một hàm nhận chuỗi và hai số nguyên làm đầu vào và trả về một chuỗi. Tức là, string -> int -> int -> string. Nó thậm chí còn tốt hơn một mẫu String.Format không đổi, bởi vì nó là một phương thức gõ mạnh cho phép tôi sử dụng lại mẫu mà không bao gồm nó trong nội tuyến.

let foo = formatFunction "test" 3 5 

Tôi càng sử dụng F #, tôi càng sử dụng nhiều ứng dụng chức năng một phần. Công cụ tuyệt vời.

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