読者です 読者をやめる 読者になる 読者になる

C# でローカル変数の再代入を禁止したいとき

.NET

とりあえずみんな一回 let 句使ってコード書いてみるべき.

var seq =
    from _ in Enumerable.Repeat(1, 1)
    let x = 1000
    let xs = new List<int>{x}
    let y = Math.PI / 2.0
    let z = "hauhau"
    select _;
var pixelsQuery =
    from y in Enumerable.Range(0, screenHeight)
    let recenterY = -(y - (screenHeight / 2.0)) / (2.0 * screenHeight)
    select from x in Enumerable.Range(0, screenWidth)
           let recenterX = (x - (screenWidth / 2.0)) / (2.0 * screenWidth)
           let point = Vector.Norm(Vector.Plus(scene.Camera.Forward, 
                                               Vector.Plus(Vector.Times(recenterX, scene.Camera.Right),
                                                           Vector.Times(recenterY, scene.Camera.Up))))
           let ray = new Ray { Start = scene.Camera.Pos, Dir = point }
           let computeTraceRay = (Func<Func<TraceRayArgs, Color>, Func<TraceRayArgs, Color>>)
            (f => traceRayArgs =>
             (from isect in
                  from thing in traceRayArgs.Scene.Things
                  select thing.Intersect(traceRayArgs.Ray)
              where isect != null
              orderby isect.Dist
              let d = isect.Ray.Dir
              let pos = Vector.Plus(Vector.Times(isect.Dist, isect.Ray.Dir), isect.Ray.Start)
              let normal = isect.Thing.Normal(pos)
              let reflectDir = Vector.Minus(d, Vector.Times(2 * Vector.Dot(normal, d), normal))
              let naturalColors = 
                  from light in traceRayArgs.Scene.Lights
                  let ldis = Vector.Minus(light.Pos, pos)
                  let livec = Vector.Norm(ldis)
                  let testRay = new Ray { Start = pos, Dir = livec }
                  let testIsects = from inter in
                                       from thing in traceRayArgs.Scene.Things
                                       select thing.Intersect(testRay)
                                   where inter != null
                                   orderby inter.Dist
                                   select inter
                  let testIsect = testIsects.FirstOrDefault()
                  let neatIsect = testIsect == null ? 0 : testIsect.Dist
                  let isInShadow = !((neatIsect > Vector.Mag(ldis)) || (neatIsect == 0))
                  where !isInShadow
                  let illum = Vector.Dot(livec, normal)
                  let lcolor = illum > 0 ? Color.Times(illum, light.Color) : Color.Make(0, 0, 0)
                  let specular = Vector.Dot(livec, Vector.Norm(reflectDir))
                  let scolor = specular > 0 
                               ? Color.Times(Math.Pow(specular, isect.Thing.Surface.Roughness), light.Color) 
                               : Color.Make(0, 0, 0)
                  select Color.Plus(Color.Times(isect.Thing.Surface.Diffuse(pos), lcolor),
                                    Color.Times(isect.Thing.Surface.Specular(pos), scolor))
              let reflectPos = Vector.Plus(pos, Vector.Times(.001, reflectDir))
              let reflectColor = 
                  traceRayArgs.Depth >= MaxDepth
                  ? Color.Make(.5, .5, .5)
                  : Color.Times(isect.Thing.Surface.Reflect(reflectPos), 
                                f(new TraceRayArgs(new Ray { Start = reflectPos, Dir = reflectDir }, 
                                                   traceRayArgs.Scene, 
                                                   traceRayArgs.Depth + 1)))
              select naturalColors.Aggregate(reflectColor, (color, natColor) => Color.Plus(color, natColor)))
                                  .DefaultIfEmpty(Color.Background).First())
           let traceRay = Y(computeTraceRay)
           select new { X = x, Y = y, Color = traceRay(new TraceRayArgs(ray, scene, 0)) };

foreach (var row in pixelsQuery)
    foreach (var pixel in row)
        setPixel(pixel.X, pixel.Y, pixel.Color.ToDrawingColor());

(追記)
そういえば『C# 3.0 と let キーワード - NyaRuRuの日記』でも色々サンプル書いていた.

FizzBuzz

var f2 = from x in Enumerable.Range(1, 100)
         let a = x % 3 == 0 ? "Fizz" : ""
         let b = x % 5 == 0 ? "Buzz" : ""
         let c = (x % 3 != 0 && x % 5 != 0) ? x.ToString() : ""
         select a + b + c;

今年の1月1日から12月31日までを列挙

var cal = from month in Enumerable.Range(1, 12)
          let year = DateTime.Today.Year
          let numDays = DateTime.DaysInMonth(year, month)
          from day in Enumerable.Range(1, numDays)
          select new DateTime(year, month, day);

2000年から2009年までの13日の金曜日は?

var ft13 = from year in Enumerable.Range(2000, 10)
           let daysInThisYear =
               from month in Enumerable.Range(1, 12)
               let numDays = DateTime.DaysInMonth(year, month)
               from day in Enumerable.Range(1, numDays)
               select new DateTime(year, month, day)
           from date in daysInThisYear
           where date.Day == 13 && date.DayOfWeek == DayOfWeek.Friday
           select date;

2000年から2009年までの各年の日曜日の数

var mysundays =
    from year in Enumerable.Range(2000, 10)
    let daysInThisYear =
        from month in Enumerable.Range(1, 12)
        let numDays = DateTime.DaysInMonth(year, month)
        from day in Enumerable.Range(1, numDays)
        select new DateTime(year, month, day)
    let sundaysInThisYear =
        from date in daysInThisYear
        where date.DayOfWeek == DayOfWeek.Sunday
        select date
    select new { Year = year, SundayCount = sundaysInThisYear.Count() };