다른 답변을 추가하기에는 너무 늦었습니까?
나는 많은 LINQ-to-objects 코드를 작성했으며 최소한 그 도메인에서 더 간단한 코드를 만드는 데 사용하기 위해 두 구문을 모두 이해하는 것이 좋다고 주장합니다. 항상 도트 구문은 아닙니다.
도트 구문이 때 물론 시간이있다 IS 길을 가야하는 것 - 다른 사람이 이러한 경우 여러 제공하고 있습니다; 그러나, 나는 당신이 원한다면 이해력이 부족하다고 생각합니다. 이해력이 유용한 샘플을 제공하겠습니다.
자릿수 대체 퍼즐에 대한 해결책은 다음과 같습니다. (LINQPad를 사용하여 작성된 솔루션이지만 콘솔 앱에서는 독립형 일 수 있음)
// 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);
... 출력 :
N = 1, O = 6, K = 4
그리 나쁘지는 않지만, 논리는 선형으로 흐르고 올바른 단일 솔루션이 나온 것을 볼 수 있습니다. 이 퍼즐은 손으로 쉽게 풀 수 있습니다 .3>> N
0, O
> 4 * N은 8> = O
> = 4를 의미합니다. 즉, 손으로 테스트 할 최대 10 개의 사례가 있음을 의미 N
합니다. O
). 나는 충분히 길을 잃었다-이 퍼즐은 LINQ 일러스트레이션 목적으로 제공된다.
컴파일러 변환
컴파일러가 이것을 동등한 도트 구문으로 변환하기 위해 많은 작업을 수행합니다. 보통 외에 두 번째 이후 from
조항이로 바뀌 얻을 SelectMany
호출 우리는이 let
될 절을 Select
사용 둘 다 돌기 전화, 투명-식별자 . 내가 보여 주려고 할 때, 도트 구문에서 이러한 식별자의 이름을 지정하는 것은 그 접근법의 가독성을 없애줍니다.
컴파일러 가이 코드를 도트 구문으로 변환 할 때 수행하는 작업을 노출시키는 트릭이 있습니다. 위의 주석 처리 된 두 줄의 주석을 해제하고 다시 실행하면 다음과 같은 결과가 나타납니다.
N = 1, O = 6, K = 4
솔루션 식 트리 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_ TransparentIdentifier0 = <> h _TransparentIdentifier0, NO = ((10 * <> h_ TransparentIdentifier0.N) + <> h _TransparentIdentifier0.O))). Select (<> h_ TransparentIdentifier1 => new <> 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_ TransparentIdentifier2.product % 10))). Where (<> h _TransparentIdentifier3 => (((<< h_ TransparentIdentifier3.K! = <> h _TransparentIdentifier3. <> h_ TransparentIdentifier2. <>h _TransparentIdentifier1. <> h_TransparentIdentifier0.O) AndAlso (<> H _TransparentIdentifier3.K! = <> H_ TransparentIdentifier3. <> H _TransparentIdentifier2. <> H_ TransparentIdentifier1. <> H _TransparentIdentifier0.N)) AndAlso ((<> H_ TransparentIdentifier3. <> H _TransparentIdentifier2. product / 10) == <> h_ TransparentIdentifier3. <> h _TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h _TransparentIdentifier0.O))). Select (<> h_ TransparentIdentifier3 => new <> f _AnonymousType4`3 (N = < > h_ TransparentIdentifier3. <> h _TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h _TransparentIdentifier0.N,O = <> h_ TransparentIdentifier3. <> h_TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h _TransparentIdentifier0.O, K = <> h__TransparentIdentifier3.K))
각 LINQ 연산자를 새 줄에 입력하여 "말할 수없는"식별자를 "말할 수있는"식별자로 변환하고, 익명 형식을 익숙한 형식으로 변경하고, AndAlso
식 트리 용어를 변경 &&
하여 컴파일러가 동등한 수준에 도달하는 변환 을 노출합니다. 도트 구문으로 :
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);
}
실행하면 다시 출력되는지 확인할 수 있습니다.
N = 1, O = 6, K = 4
...하지만 이런 코드를 작성하겠습니까?
대답은 NONBHN (아니오뿐만 아니라 지옥도 아닙니다!)입니다. 너무 복잡하기 때문입니다. 물론 "temp0".. "temp3"보다 더 의미있는 식별자 이름을 얻을 수 있지만 요점은 코드에 아무 것도 추가하지 않는다는 것입니다. 코드의 성능을 향상시키지 않고 성능을 향상시키지 않습니다. 코드를 더 잘 읽고, 코드를 못 생겼으며, 직접 작성하는 경우에는 한 번 또는 세 번 정도 엉망으로 만들 수 있습니다. 또한 "이름 게임"을 플레이하는 것은 의미있는 식별자를 찾기에 충분하지 않기 때문에 컴파일러가 쿼리 이해에서 제공하는 이름 게임에서 벗어나는 것을 환영합니다.
이 퍼즐 샘플은 실제 상황 이 아닐 수도 있습니다 . 그러나 쿼리 이해가 빛나는 다른 시나리오가 있습니다.
- 의 복잡성
Join
과 GroupJoin
: 쿼리 이해의 범위 변수의 범위 지정 join
조항 그렇지 않으면 이해 구문에 컴파일 시간 오류에 도트 구문으로 컴파일 할 수있는 실수를 켜십시오.
- 컴파일러가 이해 변환에 투명한 식별자를 도입 할 때마다 이해가 가치가 있습니다. 여기에는 다중
from
절, join
& join..into
절 및 let
절 중 하나를 사용하는 것이 포함됩니다 .
저는 고향에서 이해 구문 을 위반 한 하나 이상의 엔지니어링 샵을 알고 있습니다. 나는 이것이 이해 구문이 도구이자 유용한 도구이므로 유감이라고 생각합니다. 나는 그것이 말처럼 많은 생각 "당신이 끌 함께 할 수없는 드라이버로 할 수있는 일이 있습니다. 당신은 부정 행위로 드라이버를 사용할 수 있기 때문에, 끌이 왕의 칙령에 따라 이제부터 금지된다."