No problemo in Ruby:
def map_unless_nested(arr)
arr.map {|v| return [] if v.is_a? Array; yield v}
end
map_unless_nested([2,3,4,5]) {|v| v+1 }
# => [3,4,5,6]
map_unless_nested([2,[3,4],5]) {|v| v+1 }
# => []
This starts looking ugly in JavaScript if we want to generalize "map". Either we can keep "map" clean and wrap our callback so it throws exceptions on array input: function map(arr, callback) {
var ret = [];
for (var i = 0; i < arr.length; i++) {
ret.push(callback(arr[i]));
}
return ret;
}
function map_unless_nested(arr, callback) {
try {
return map(arr, function(v) {
if (v instanceof Array) throw "nested!";
return callback(v);
});
} catch (e) {
if (e==="nested!") { return []; }
throw e;
}
}
map_unless_nested([2,3,4,5], function(v){ return v + 1; })
// => [3,4,5,6]
map_unless_nested([2,[3,4],5], function(v){ return v + 1; })
// => []
Or, if we want to keep the callback clean, we have to start fiddling with map, and then it's no longer really just map (it's map with halting parameters), and then you're using special return values to inform calling functions that the halting condition was met. function map_unless_test(arr, callback, test) {
var ret = [];
for (var i = 0; i < arr.length; i++) {
if (test(arr[i])) { return false; }
ret.push(callback(arr[i]));
}
return ret;
}
function map_unless_nested(arr, callback) {
return map_unless_test(arr, callback, function(v) {
return v instanceof Array;
}) || [];
}
If you still think that exceptions are the best language feature to solve this example, consider the situation where you want to do something with those nested arrays. For example, let's take the Ruby example and modify it so it will return a copy of the first-encountered innermost array with the callback applied. def map_first_innermost(arr, &block)
arr.map do |v|
return map_first_innermost(v, &block) if v.is_a? Array
yield v
end
end
That was easy. You can see that non-local returns become useful very quickly.