我在Sprache 存盤庫中遇到了以下代碼:
Parser<string> identifier =
from leading in Parse.WhiteSpace.Many()
from first in Parse.Letter.Once().Text()
from rest in Parse.LetterOrDigit.Many().Text()
from trailing in Parse.WhiteSpace.Many()
select first rest;
var id = identifier.Parse(" abc123 ");
我在這里看到一個矛盾:from條款檔案說來源(Parse.WhiteSpace.Many()或Parse.Letter.Once().Text()在我們的例子中)必須是IEnumerable:
from 子句中參考的資料源的型別必須為
IEnumerable,IEnumerable<T>或派生型別,例如IQueryable<T>
但事實并非如此,編譯器說這很好!
我認為對 IEnumerable 有一些隱式強制轉換,但沒有:Parse.WhiteSpace.Many()回傳Parser<IEnumerable<T>>和Parse.Letter.Once().Text()回傳Parser<string>(型別不是IEnumerable)。
第一個問題:為什么編譯器允許這段代碼?
此外,最終運算式select first rest沒有考慮leading和trailing變數,但最終結果identifier肯定會在內部使用它們。
第二個問題:通過什么規則\機制leading和trailing變數被添加到identifier?
PS 如果有人分享一份關于 LINQ 查詢語法內部作業的包羅萬象的檔案,那就太好了。我沒有找到關于這個話題的任何東西。
uj5u.com熱心網友回復:
在查看了我觀察到的代碼五分鐘后:
parser 是回傳中間結果的委托
public delegate IResult<T> Parser<out T>(IInput input);宣告了允許 linq 語法的 linq 兼容方法,例如:
public static Parser<U> Select<T, U>(this Parser<T> parser, Func<T, U> convert) { if (parser == null) throw new ArgumentNullException(nameof(parser)); if (convert == null) throw new ArgumentNullException(nameof(convert)); return parser.Then(t => Return(convert(t))); }
https://github.com/sprache/Sprache/blob/develop/src/Sprache/Parse.cs#L357
IEnumerable語法需要介面from x in set才能正常作業,您只需要具有正確名稱并接受正確引數集的特定擴展方法是不正確的。因此,上述內容使 select 有效。在這里你有 where 方法
public static Parser<T> Where<T>(this Parser<T> parser, Func<T, bool> predicate)
{
if (parser == null) throw new ArgumentNullException(nameof(parser));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
return i => parser(i).IfSuccess(s =>
predicate(s.Value) ? s : Result.Failure<T>(i,
$"Unexpected {s.Value}.",
new string[0]));
}
https://github.com/sprache/Sprache/blob/develop/src/Sprache/Parse.cs#L614
等等。
這是 linq 抽象的單獨實作,與集合無關,它與決議文本有關。它生成一個嵌套的委托鏈,處理給定的字串以驗證它是否符合特定的語法并回傳描述決議文本的結構。
這回答了第一個問題。對于第二個,您需要遵循代碼:from x in set除了第一個映射到SelectMany函式之外的所有:
public static Parser<V> SelectMany<T, U, V>(
this Parser<T> parser,
Func<T, Parser<U>> selector,
Func<T, U, V> projector)
{
if (parser == null) throw new ArgumentNullException(nameof(parser));
if (selector == null) throw new ArgumentNullException(nameof(selector));
if (projector == null) throw new ArgumentNullException(nameof(projector));
return parser.Then(t => selector(t).Select(u => projector(t, u)));
}
https://github.com/sprache/Sprache/blob/develop/src/Sprache/Parse.cs#L635
然后方法 https://github.com/sprache/Sprache/blob/develop/src/Sprache/Parse.cs#L241
在那里你會看到如果第一個成功(匹配的前導空格)只有第二個(單字母決議器)應用于字串的其余部分。所以同樣,它不是一個集合,它處理導致決議字串的一系列事件。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/471250.html
上一篇:性能LINQ異步等待
下一篇:C#取消參考可能為空的參考
