Tôi đã dán mã của bạn vào một tệp tco.fs. Tôi đã thêm một hàm applyRule để làm cho nó có thể biên dịch được.
tco.fs
let applyRule rule exp =
Some ""
let rec applyAll rules expr =
rules
|> List.fold (fun state rule ->
match state with
| Some e ->
match applyRule rule e with
| Some newE -> Some newE
| None -> Some e
| None -> applyRule rule expr) None
|> Option.bind (applyAll rules)
Sau đó, tôi đã thực hiện một tập tin batch để phân tích IL.
compile_and_dasm.bat
SET ILDASM="C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools\ildasm.exe"
Fsc tco.fs
%ILDASM% /out=tco.il /NOBAR /tok tco.exe
Là một đầu ra chúng tôi tìm ra tco.il chứa IL. Chức năng liên quan là ở đây.
.method /*06000002*/ public static class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpOption`1/*01000003*/<!!b>
applyAll<a,b>(class [FSharp.Core/*23000002*/]Microsoft.FSharp.Collections.FSharpList`1/*01000008*/<!!a> rules,
string expr) cil managed
{
.custom /*0C000003:0A000003*/ instance void [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute/*01000007*/::.ctor(int32[]) /* 0A000003 */ = (01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00)
// Code size 26 (0x1a)
.maxstack 8
IL_0000: ldarg.0
IL_0001: newobj instance void class Tco/*02000002*//[email protected]/*02000003*/<!!b,!!a>/*1B000004*/::.ctor(class [FSharp.Core/*23000002*/]Microsoft.FSharp.Collections.FSharpList`1/*01000008*/<!1>) /* 0A000004 */
IL_0006: newobj instance void class Tco/*02000002*//'[email protected]'/*02000004*/<!!a>/*1B000005*/::.ctor() /* 0A000005 */
IL_000b: ldnull
IL_000c: ldarg.0
IL_000d: call !!1 [FSharp.Core/*23000002*/]Microsoft.FSharp.Collections.ListModule/*01000009*/::Fold<!!0,class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpOption`1/*01000003*/<string>>(class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpFunc`2/*01000002*/<!!1,class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpFunc`2/*01000002*/<!!0,!!1>>,
!!1,
class [FSharp.Core/*23000002*/]Microsoft.FSharp.Collections.FSharpList`1/*01000008*/<!!0>) /* 2B000001 */
IL_0012: tail.
IL_0014: call class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpOption`1/*01000003*/<!!1> [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.OptionModule/*0100000A*/::Bind<string,!!1>(class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpFunc`2/*01000002*/<!!0,class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpOption`1/*01000003*/<!!1>>,
class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpOption`1/*01000003*/<!!0>) /* 2B000002 */
IL_0019: ret
} // end of method Tco::applyAll
Chúng tôi thấy ở đây rằng mã opcode đuôi được tạo. Đây là một gợi ý từ trình biên dịch IL tới trình biên dịch JIT (mà thực sự tạo ra mã máy thực thi), rằng một cuộc gọi đuôi có thể được thực hiện ở đây.
Cho dù cuộc gọi đuôi thực sự được thực hiện như vậy là tùy thuộc vào trình biên dịch JIT, có thể đọc được here.
Bạn có thể tìm trong IL và xem những gì được tạo? Tôi thấy một 'đuôi .' :). – DaveShaw
Tôi đã tự do để định dạng lại mã của bạn một chút. Đặt toán tử '|>' ở bất kỳ nơi nào khác ngoài trực tiếp trước hàm bạn đang "chuyển vào" là một cách chắc chắn để loại bỏ 99,5% các lập trình viên F #. ;-) – TeaDrivenDev
Xem http://stackoverflow.com/a/40165030/82959 để biết thêm thông tin về các cuộc gọi đuôi với '(|>)' - kết quả của bạn có thể thay đổi giữa chế độ gỡ lỗi và chế độ phát hành. – kvb