Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> It's hard to feel happy grinding out a contrived LINQ expression for the same result as a "practically English" list comprehension in Python.

I feel the other way. LINQ is great for building pipelines. List comprehensions are kinda like English, but it gets ugly when you use the flattening feature, or when you want something more than just map and filter.

Looking at their C# code, they’re going at it in a fairly low-level way, with a lot of aliases for numeric types and a lot of casting between them. For example:

    private static f32 distinct_arrangements_for(DieVal[] dieval_vec) { 
        var key_counts = dieval_vec.GroupBy(x=>x).Select(g=>(g.Key, (u8)g.Count()));
        uint divisor=1;
        uint non_zero_dievals=0;
        foreach (var (key, count) in key_counts){  
            if (key != 0){  
                divisor *= factorial(count);
                non_zero_dievals += count;
            } 
        } 
        return factorial(non_zero_dievals) / divisor;
    } 
If you take out the unnecessary number type abuse and just use `int` everywhere, you get something much clearer:

    private static float DistinctArrangementsFor(DieVal[] dievals) { 
        var dievalCounts = dievals
            .GroupBy(x => x)
            .Where(group => group.Key != 0)
            .Select(group => group.Count())
            .ToArray();
        var divisor = dievalCounts
            .Select(factorial)
            .Aggregate(1, (a, b) => a * b);
        var nonZeroDievals = dievalCounts.Sum();
        return factorial(nonZeroDievals) / divisor;
    }
Also, even with all this hard work with weird types, this function has a suspicious return type, since it does integer division and returns a float.

There are a few more weird things, like not using switch statements/switch expressions, or this function:

    // calculate relevant counts for gamestate: required lookups and saves
    public int counts() { 
        var ticks = 0; 
        var false_true = new bool[] {true, false};
        var just_false = new bool[] {false};
        foreach (var subset_len in Range(1,open_slots.Count)){
            var combos = open_slots.Combinations(subset_len);
            foreach (var slots_vec in combos ) { 
                var slots = new Slots(slots_vec.ToArray());
                var joker_rules = slots.has(YAHTZEE); // yahtzees aren't wild whenever yahtzee slot is still available 
                var totals = Slots.useful_upper_totals(slots); 
                foreach (var _ in totals) {
                    foreach (var __ in joker_rules? false_true : just_false ){
                        // var slot_lookups = (subset_len * subset_len==1? 1 : 2) * 252; // * subset_len as u64;
                        // var dice_lookups = 848484; // // previoiusly verified by counting up by 1s in the actual loop. however chunking forward is faster 
                        // lookups += (dice_lookups + slot_lookups); this tends to overflow so use "normalized" ticks below
                        ticks++; // this just counts the cost of one pass through the bar.tick call in the dice-choose section of build_cache() loop
        } } } }
        return (int)ticks;
    } 
There are a few weird things, like the pointless foreach loops at the end (which could be replaced with a bit of arithmetic) or the use of a foreach loop where a regular for loop would be better.

If this code was rewritten in more idiomatic C#, it could be the basis of a good comparison between languages’ performance and syntax. But I’m not sure if it’s a good example at this point.



Agree your example is more elegant than mine, and LINQ beats a list comprehension for more complex needs.

counts() was a throw-away for initializing the progress bar, so I was a little lazy there

Float you mention was needed for the looping calc at the call site.

Appreciate the feedback




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: