Có bao giờ là quá muộn để thêm một câu trả lời?
Tôi đã viết rất nhiều mã LINQ-to-object và tôi cho rằng ít nhất trong miền đó phải hiểu cả hai cú pháp để sử dụng bất kỳ mã nào tạo ra mã đơn giản hơn - không phải luôn luôn là cú pháp dấu chấm.
Tất nhiên, có những lúc cú pháp chấm là cách để đi - những người khác đã cung cấp một số trường hợp này; tuy nhiên, tôi nghĩ rằng sự hiểu biết đã được thay đổi ngắn - nếu bạn đọc rap tệ, nếu bạn muốn. Vì vậy, tôi sẽ cung cấp một mẫu mà tôi tin rằng sự hiểu biết là hữu ích.
Đây là một giải pháp cho câu đố thay thế chữ số: (giải pháp được viết bằng LINQPad, nhưng có thể độc lập trong ứng dụng bảng điều khiển)
// NO
// NO
// NO
//+NO
//===
// OK
var solutions =
from O in Enumerable.Range(1, 8) // 1-9
//.AsQueryable()
from N in Enumerable.Range(1, 8) // 1-9
where O != N
let NO = 10 * N + O
let product = 4 * NO
where product < 100
let K = product % 10
where K != O && K != N && product / 10 == O
select new { N, O, K };
foreach(var i in solutions)
{
Console.WriteLine("N = {0}, O = {1}, K = {2}", i.N, i.O, i.K);
}
//Console.WriteLine("\nsolution expression tree\n" + solutions.Expression);
... Kết quả nào:
N = 1, O = 6, K = 4
Không quá tệ, logic chảy theo tuyến tính và chúng ta có thể thấy rằng nó đưa ra một giải pháp đúng duy nhất. Câu đố này đủ dễ để giải bằng tay: lý do rằng 3 >> N
0 và O
> 4 * N ngụ ý 8> = O
> = 4. Điều đó có nghĩa là có tối đa 10 trường hợp để kiểm tra bằng tay (2 cho N
-by- 5 cho O
). Tôi đã đi lạc đủ - câu đố này được cung cấp cho mục đích minh họa LINQ.
Chuyển đổi trình biên dịch
Có rất nhiều trình biên dịch làm điều này để dịch nó thành cú pháp dấu chấm tương đương. Bên cạnh các mệnh đề thứ hai và tiếp theofrom
SelectMany
thông thường được chuyển thành các cuộc gọi, chúng ta có let
các mệnh đề trở thành Select
các cuộc gọi với các phép chiếu, cả hai đều sử dụng định danh trong suốt . Như tôi sắp trình bày, việc đặt tên cho các định danh này theo cú pháp dấu chấm sẽ làm mất khả năng đọc của phương pháp đó.
Tôi có một mẹo để phơi bày những gì trình biên dịch làm trong việc dịch mã này thành cú pháp chấm. Nếu bạn bỏ ghi chú hai dòng nhận xét ở trên và chạy lại, bạn sẽ nhận được kết quả đầu ra sau:
N = 1, O = 6, K = 4
cây biểu thức giải pháp System.Linq.Enumerable + d_ b8.SelectMany (O => Range (1, 8), (O, N) => new <> f _AnonymousType0 2(O = O, N = N)).Where(<>h__TransparentIdentifier0 => (<>h__TransparentIdentifier0.O != <>h__TransparentIdentifier0.N)).Select(<>h__TransparentIdentifier0 => new <>f__AnonymousType1
2 (<> h_ Trong suốtIdentifier0 = <> h _TransparentIdentifier, NO = ((10 * <> h_ TransparentIdentifier0.N) + <> h _TransparentIdentifier0.O))). Select (<> h_ TransparentIdentifier1 => mới <> f _AnonymousType2 2(<>h__TransparentIdentifier1 = <>h__TransparentIdentifier1, product = (4 * <>h__TransparentIdentifier1.NO))).Where(<>h__TransparentIdentifier2 => (<>h__TransparentIdentifier2.product < 100)).Select(<>h__TransparentIdentifier2 => new <>f__AnonymousType3
2 (<> h_ TransparentIdentifier2 = <> h _TransparentIdentifier2, K = ( <> h_ Trong suốtIdentifier2.product % 10))). Trong đó (<> h _TransparentIdentifier3 => (((<> h_ Trong suốtIdentifier3.K ! = <> h _TransparentIdentifier3. <> h_ Minh bạchh _TransparentIdentifier1. <> h_TransparentIdentifier0.O) AndAlso (<> h _TransparentIdentifier3.K! = <> H_ TransparentIdentifier3. <> H _TransparentIdentifier2. <> H_ TransparentIdentifier1. <> H _TransparentIdentifier0.N)) AndAlso ((<> h_ TransparentIdentifier3. <> H _TransparentIdentifier2. sản phẩm / 10) == <> h_ Trong suốtIdentifier3 . <> h _TransparentIdentifier2. <> h_ Trong suốtIdentifier1 . <> h _TransparentIdentifier0.O))). Chọn (<> h_ suốtIdentifier3 => new < > h_ Trong suốtIdentifier3 . <> h _TransparentIdentifier2. <> h_ Trong suốtIdentifier1 . <> h _TransparentIdentifier0.N,O = <> h_ Trong suốtIdentifier3 . <> H_TransparentIdentifier2. <> H_ Trong suốtIdentifier1 . <> H _TransparentIdentifier0.O, K = <> h__TransparentIdentifier3.K))
Đặt mỗi toán tử LINQ trên một dòng mới, dịch các số nhận dạng "không thể nói được" sang các số nhận dạng mà chúng ta có thể "nói", thay đổi các loại ẩn danh thành dạng quen thuộc của chúng và thay đổi AndAlso
biệt ngữ cây biểu thức để &&
hiển thị các biến đổi mà trình biên dịch thực hiện để tương đương trong cú pháp dấu chấm:
var solutions =
Enumerable.Range(1,8) // from O in Enumerable.Range(1,8)
.SelectMany(O => Enumerable.Range(1, 8), (O, N) => new { O = O, N = N }) // from N in Enumerable.Range(1,8)
.Where(temp0 => temp0.O != temp0.N) // where O != N
.Select(temp0 => new { temp0 = temp0, NO = 10 * temp0.N + temp0.O }) // let NO = 10 * N + O
.Select(temp1 => new { temp1 = temp1, product = 4 * temp1.NO }) // let product = 4 * NO
.Where(temp2 => temp2.product < 100) // where product < 100
.Select(temp2 => new { temp2 = temp2, K = temp2.product % 10 }) // let K = product % 10
.Where(temp3 => temp3.K != temp3.temp2.temp1.temp0.O && temp3.K != temp3.temp2.temp1.temp0.N && temp3.temp2.product / 10 == temp3.temp2.temp1.temp0.O)
// where K != O && K != N && product / 10 == O
.Select(temp3 => new { N = temp3.temp2.temp1.temp0.N, O = temp3.temp2.temp1.temp0.O, K = temp3.K });
// select new { N, O, K };
foreach(var i in solutions)
{
Console.WriteLine("N = {0}, O = {1}, K = {2}", i.N, i.O, i.K);
}
Mà nếu bạn chạy, bạn có thể xác minh rằng nó lại xuất ra:
N = 1, O = 6, K = 4
... nhưng bạn có bao giờ viết mã như thế này không?
Tôi muốn đặt câu trả lời là NONBHN (Không chỉ Không, mà là Không!) - vì nó quá phức tạp. Chắc chắn bạn có thể đưa ra một số tên định danh có ý nghĩa hơn "temp0" .. "temp3", nhưng vấn đề là họ không thêm bất cứ điều gì vào mã - họ không làm cho mã hoạt động tốt hơn, họ không làm làm cho mã đọc tốt hơn, chúng chỉ làm xấu mã và nếu bạn đang làm bằng tay, chắc chắn bạn sẽ làm hỏng nó một hoặc ba lần trước khi làm cho đúng. Ngoài ra, chơi "trò chơi tên" là đủ khó cho các định danh có ý nghĩa, vì vậy tôi hoan nghênh sự phá vỡ trò chơi tên mà trình biên dịch cung cấp cho tôi trong việc hiểu các truy vấn.
Mẫu câu đố này có thể không đủ thực tế để bạn thực hiện nghiêm túc; tuy nhiên, các kịch bản khác tồn tại khi sự hiểu biết truy vấn tỏa sáng:
- Độ phức tạp của
Join
và GroupJoin
: phạm vi của các biến phạm vi trong join
mệnh đề hiểu truy vấn biến các lỗi có thể biên dịch theo cú pháp dấu chấm thành lỗi thời gian biên dịch theo cú pháp hiểu.
- Bất cứ khi nào trình biên dịch sẽ giới thiệu một định danh minh bạch trong biến đổi hiểu, việc hiểu trở nên đáng giá. Điều này bao gồm việc sử dụng bất kỳ những điều sau đây: nhiều
from
khoản, join
& join..into
điều khoản và let
điều khoản.
Tôi biết nhiều hơn một cửa hàng kỹ thuật ở quê tôi có cú pháp hiểu hiểu ngoài vòng pháp luật . Tôi nghĩ rằng đây là một điều đáng tiếc vì cú pháp hiểu là một công cụ và một công cụ hữu ích ở đó. Tôi nghĩ nó rất giống với câu nói: "Có những điều bạn có thể làm với một cái tuốc nơ vít mà bạn không thể làm với cái đục. Bởi vì bạn có thể sử dụng một cái tuốc nơ vít như một cái đục, từ đó bị cấm theo sắc lệnh của nhà vua."