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

It was just the first example that came to mind to illustrate the general pattern. We could also make this particular example shorter by using your naming conventions and writing it in a more functional manner instead of mutating the list:

    function unique(list) {
        return list.reduce((r, i) => r.includes(i) ? r : r.concat(i), []);
    }
Clearer? I dunno; probably depends on the reader's background and preferences.



I still don't see what you are gaining here. Outside of code golf, the goal should not be to try and write code as short as possible, regardless of the language you are using. But this just seems to validate Pike's assertion that a for loop is more suitable to the problem.

Perhaps an example of where map/reduce is a significant improvement to the expressiveness would be appropriate for the discussion?


I wrote an aimbot for a FPS game.

The basic idea:

Take all the players, and all the buildings.

Filter out those that aren't enemies.

Filter out those that are currently invincible.

Transform that into a list of actual points in worldspace- the hitboxes for players, the AABB centers for buildings. (Not all player models have the same hitbox count.) Unless we are holding a weapon that does splash damage- then go for the feet on players (their origin). And if we hold a projectile weapon, do prediction based on the player's velocity.

Now transform each point into a 2-tuple (position, score), based on some heuristics implemented in another function.

Do a raycast to each point. Filter out those that can't be hit.

Take the point with the highest score, if there is one, and set our viewangles to aim at it. Otherwise leave them alone.

The actual implementation of this looked something like this:

  let target = get_players().chain(get_buildings)
               .filter(|e| are_enemies(me, e))
               .filter(is_vulnerable)
               .flat_map(entity_to_scored_aimpoints)
               .filter(|(score, point)| trace(me_eyes_predicted, point).fraction > 0.999)
               .max_by(|(score, point)| score);
  if let Some(target) = target {
     aimray = target - me_eyes_predicted;
     viewangles = vector_to_angles(aimray);
  }
(Note that max_by is just a special case of reduce/fold; in my experience, you rarely want to use reduce directly; there's probably a more ergonomic wrapper. Sometimes you do, though.)

To me, that's pretty readable (stuff specific to the game aside, like the trace.fraction ugliness- fraction is "how far" the trace got before hitting something, 1.0 meaning there's nothing in the way. the comparison is to handle some floating-point inaccuracy there), and handles some really annoying cases properly.


I agree wholeheartedly with the notion that you should rarely use reduce directly. It is much less useful than map or filter.

Suppose that you have a bunch of things implemented using map or filter. When someone writes parallelized versions of map and filter, all of the existing code gets the benefits.

Now suppose you have a bunch of basic functions implemented using reduce (sum, product, min, max, reverse, ...). Can these be parallelized? Yes - by throwing away the 'reduce' implementation, and starting from scratch.

The problem with reduce, compared to its more useful cousins map and filter, is that it is too powerful. Map and filter are more limited than reduce, but if you can express your computation in terms of maps and filters, you get something valuable in return. If you can express is in terms of reduce, you save a few keystrokes, and that's about it.

For anyone interested in this kind of stuff, I recommend Guy Steele's talk "Organizing Functional Code for Parallel Execution; or, foldl and foldr Considered Slightly Harmful": https://vimeo.com/6624203


Reduce can be parallelized when the reducing function is associative. You can split the input sequence into chunks that are reduced in parallel et merge the results with another sequential reduce.

https://lparallel.org/preduce/


I like that the function in your link is called preduce and not just reduce. Reduce has a standard definition, which doesn't require associativity. To eliminate confusion, a function that does require associativity deserves a different name, just like here.

And using these names, I would say that preduce seems much more useful to me than reduce.




Join us for AI Startup School this June 16-17 in San Francisco!

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

Search: