Generator/Exponential: Difference between revisions
Content added Content deleted
(→{{header|Python}}: (Compositional version) retired fmapGen for vanilla `map` (which works over generators)) |
(→{{header|JavaScript}}: Added a functional version, showing compositional derivation of custom generators.) |
||
Line 1,558: | Line 1,558: | ||
=={{header|JavaScript}}== |
=={{header|JavaScript}}== |
||
===Procedural=== |
|||
{{works with|Firefox 3.6 using JavaScript 1.7|}} |
{{works with|Firefox 3.6 using JavaScript 1.7|}} |
||
Line 1,600: | Line 1,601: | ||
</lang> |
</lang> |
||
===ES6=== |
====ES6==== |
||
<lang JavaScript>function* nPowerGen(n) { |
<lang JavaScript>function* nPowerGen(n) { |
||
let e = 0; |
let e = 0; |
||
Line 1,635: | Line 1,636: | ||
1024 |
1024 |
||
1089</pre> |
1089</pre> |
||
===Functional=== |
|||
Compositional derivation of custom generators: |
|||
{{Works with|JavaScript|ES6}} |
|||
<lang javascript>(() => { |
|||
'use strict'; |
|||
// main :: IO() |
|||
const main = () => { |
|||
// powers :: Gen [Int] |
|||
const powers = n => |
|||
fmapGen( |
|||
x => Math.pow(x, n), |
|||
enumFrom(0) |
|||
); |
|||
// xs :: [Int] |
|||
const xs = take(10, drop(20, |
|||
differenceGen( |
|||
powers(2), |
|||
powers(3) |
|||
) |
|||
)); |
|||
console.log(xs); |
|||
// -> [529,576,625,676,784,841,900,961,1024,1089] |
|||
}; |
|||
// GENERIC FUNCTIONS ---------------------------------- |
|||
// Just :: a -> Maybe a |
|||
const Just = x => ({ |
|||
type: 'Maybe', |
|||
Nothing: false, |
|||
Just: x |
|||
}); |
|||
// Nothing :: Maybe a |
|||
const Nothing = () => ({ |
|||
type: 'Maybe', |
|||
Nothing: true, |
|||
}); |
|||
// Tuple (,) :: a -> b -> (a, b) |
|||
const Tuple = (a, b) => ({ |
|||
type: 'Tuple', |
|||
'0': a, |
|||
'1': b, |
|||
length: 2 |
|||
}); |
|||
// differenceGen :: Gen [a] -> Gen [a] -> Gen [a] |
|||
function* differenceGen(ga, gb) { |
|||
// All values of generator stream a except any |
|||
// already seen in generator stream b. |
|||
const |
|||
stream = zipGen(ga, gb), |
|||
sb = new Set([]); |
|||
while (true) { |
|||
const xy = take(1, stream); |
|||
if (0 < xy.length) { |
|||
const [x, y] = Array.from(xy[0]); |
|||
sb.add(y); |
|||
if (!sb.has(x)) yield x |
|||
} |
|||
} |
|||
}; |
|||
// drop :: Int -> [a] -> [a] |
|||
// drop :: Int -> Generator [a] -> Generator [a] |
|||
// drop :: Int -> String -> String |
|||
const drop = (n, xs) => |
|||
Infinity > length(xs) ? ( |
|||
xs.slice(n) |
|||
) : (take(n, xs), xs); |
|||
// enumFrom :: Enum a => a -> [a] |
|||
function* enumFrom(x) { |
|||
let v = x; |
|||
while (true) { |
|||
yield v; |
|||
v = 1 + v; |
|||
} |
|||
} |
|||
// fmapGen <$> :: (a -> b) -> Gen [a] -> Gen [b] |
|||
function* fmapGen(f, gen) { |
|||
let v = take(1, gen); |
|||
while (0 < v.length) { |
|||
yield(f(v[0])) |
|||
v = take(1, gen) |
|||
} |
|||
} |
|||
// fst :: (a, b) -> a |
|||
const fst = tpl => tpl[0]; |
|||
// Returns Infinity over objects without finite length. |
|||
// This enables zip and zipWith to choose the shorter |
|||
// argument when one is non-finite, like cycle, repeat etc |
|||
// length :: [a] -> Int |
|||
const length = xs => |
|||
(Array.isArray(xs) || 'string' === typeof xs) ? ( |
|||
xs.length |
|||
) : Infinity; |
|||
// snd :: (a, b) -> b |
|||
const snd = tpl => tpl[1]; |
|||
// take :: Int -> [a] -> [a] |
|||
// take :: Int -> String -> String |
|||
const take = (n, xs) => |
|||
'GeneratorFunction' !== xs.constructor.constructor.name ? ( |
|||
xs.slice(0, n) |
|||
) : [].concat.apply([], Array.from({ |
|||
length: n |
|||
}, () => { |
|||
const x = xs.next(); |
|||
return x.done ? [] : [x.value]; |
|||
})); |
|||
// uncons :: [a] -> Maybe (a, [a]) |
|||
const uncons = xs => { |
|||
const lng = length(xs); |
|||
return (0 < lng) ? ( |
|||
lng < Infinity ? ( |
|||
Just(Tuple(xs[0], xs.slice(1))) // Finite list |
|||
) : (() => { |
|||
const nxt = take(1, xs); |
|||
return 0 < nxt.length ? ( |
|||
Just(Tuple(nxt[0], xs)) |
|||
) : Nothing(); |
|||
})() // Lazy generator |
|||
) : Nothing(); |
|||
}; |
|||
// zipGen :: Gen [a] -> Gen [b] -> Gen [(a, b)] |
|||
const zipGen = (ga, gb) => { |
|||
function* go(ma, mb) { |
|||
let |
|||
a = ma, |
|||
b = mb; |
|||
while (!a.Nothing && !b.Nothing) { |
|||
let |
|||
ta = a.Just, |
|||
tb = b.Just |
|||
yield(Tuple(fst(ta), fst(tb))); |
|||
a = uncons(snd(ta)); |
|||
b = uncons(snd(tb)); |
|||
} |
|||
} |
|||
return go(uncons(ga), uncons(gb)); |
|||
}; |
|||
// MAIN --- |
|||
return main(); |
|||
})();</lang> |
|||
{{Out}} |
|||
<pre>[529,576,625,676,784,841,900,961,1024,1089]</pre> |
|||
=={{header|jq}}== |
=={{header|jq}}== |