Hacker News new | past | comments | ask | show | jobs | submit login

I've come up with a function group-reduce which generalizes the construction of hashes from sequences, while streamlining the syntax. The idea is that the hash table entries are accumulators, and we can combine the grouping operation which distributes items into hash keys with reduction over the accumulators. For instance, take the integers 1 through 10, group them into even and odd, and sum the groups:

  1> [group-reduce (hash) evenp + (range 1 10) 0]
  #H(() (t 30) (nil 25))
Hence, evens add to 30, odds to 25.

Now with this, we can obtain a histogram easily, because a left (or right) reduce/fold can count items:

  2> [reduce-left (do inc @1)
       '(brown red green yellow yellow brown brown black) 0]
  8
The (do inc @1) gives us (lambda (blah . rest) (inc blah)) which can take two arguments (suitable for reduce) and just returns (+ 1 blah). We can use this reducer function with group-reduce:

  3> [group-reduce (hash) identity (do inc @1)
       '(brown red green yellow yellow brown brown black) 0]
  #H(() (black 1) (yellow 2) (green 1) (red 1) (brown 3))
group-reduce will be in the next release of TXR (124).

I deliberately made it accept an existing hash so it can be called repeatedly on the same hash to accumulate multiple jobs. That also answers the question of where to specify the hash table attributes.

One last detail is that there is an optional argument we are omitting in the group-by call: a filter function which is optionally applied to each element in the table after the accumulation is done. For instance, we can specify this as nreverse, and then we can express group-by using group-reduce.

First naive attempt: oops cons arguments wrong way:

  1> [group-reduce (hash) evenp cons (range 1 10) nil]
  #H(() (t (((((nil . 2) . 4) . 6) . 8) . 10)) (nil (((((nil . 1) . 3) . 5) . 7) . 9)))
Fix with flipargs. Better, but the groups are consed up in reverse:

  2> [group-reduce (hash) evenp [flipargs cons] (range 1 10) nil]
  #H(() (t (10 8 6 4 2)) (nil (9 7 5 3 1)))
Now the optional argument kicks in to fix this:

  3> [group-reduce (hash) evenp [flipargs cons] (range 1 10) nil nreverse]
  #H(() (t (2 4 6 8 10)) (nil (1 3 5 7 9)))
Compare with group-by:

  4> [group-by evenp (range 1 10)]
  #H(() (t (2 4 6 8 10)) (nil (1 3 5 7 9)))



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

Search: