Last active
August 29, 2015 14:01
-
-
Save cawhitworth/80908cdb1bdfac5f6af5 to your computer and use it in GitHub Desktop.
Calculate your Eddington number
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Given this in Lisp: | |
-- (defn eddington [a] (count (filter true? (keep-indexed < (reverse (sort a)))))) | |
-- do it in Haskell (thanks to @zimpenfish for the original) | |
import Data.List | |
-- keep-indexed f l applies f over l and keeps the result of anything that is | |
-- not nil. f takes two arguments, index of list item and list item | |
-- We can do this in Haskell by zipping the original list with the indices | |
-- then mapping over it (being a Real Language, we don't have nil, so the | |
-- operation is actually a map - we can't throw away things that don't exist :) | |
with_index l = zip [0..length l] l | |
pair_apply f p = f (fst p) (snd p) | |
map_indexed f l = map (pair_apply f) $ with_index l | |
-- and so we can re-implement the algorithm thus: | |
eddington l = | |
let | |
mapped = map_indexed (<) $ reverse (sort l) | |
in | |
length $ filter id mapped | |
-- But hold on, that map to true/false followed by a filter can be | |
-- turned into a simple filter | |
filter_indexed f l = map snd $ filter (pair_apply f) $ with_index l | |
-- Giving... | |
eddington2 l = | |
length $ filter_indexed (<) $ reverse (sort l) | |
-- (or, and I think this is equivalent, you can use takeWhile, because | |
-- the once the index exceeds the entry in the reverse-sorted list, | |
-- it can never be lower than it again, so we can benefit from early | |
-- termination and use takeWhile instead) | |
take_indexed f l = map snd $ takeWhile (pair_apply f) $ with_index l | |
eddington2b l = | |
length $ take_indexed (<) $ reverse (sort l) | |
-- Sooo.... | |
transform_indexed f g l = map snd $ f (pair_apply g) $ with_index l | |
-- (which means we can define...) | |
take_indexed2 f l = transform_indexed takeWhile f l | |
filter_indexed2 f l = transform_indexed filter f l | |
-- And thus we get... | |
eddington3 l = | |
length $ transform_indexed filter (<) $ reverse (sort l) | |
eddington3b l = | |
length $ transform_indexed takeWhile (<) $ reverse (sort l) | |
-- But we don't actually care about the result of the filter/map, | |
-- just the length of the list | |
eddington4 l = | |
length $ filter (pair_apply (<)) $ with_index $ reverse (sort l) | |
eddington4b l = | |
length $ takeWhile (pair_apply (<)) $ with_index $ reverse (sort l) | |
-- And so if we substitute in for our other functions | |
eddington_oneline l = | |
length $ filter (\p -> fst p < snd p) $ zip [0..length l] $ reverse (sort l) | |
eddington_oneline_b l = | |
length $ takeWhile (\p -> fst p < snd p) $ zip [0..length l] $ reverse (sort l) | |
-- Or if you prefer brackets | |
eddington_lispy l = | |
length (filter (\p -> fst p < snd p) (zip [0..length l] (reverse (sort l)))) | |
eddington_lispy_b l = | |
length (takeWhile (\p -> fst p < snd p) (zip [0..length l] (reverse (sort l)))) | |
-- Of course, composing it out of more general utility functions is nicer | |
-- and expresses intent more cleanly; the original was a one-liner and it's | |
-- nice to see we can do it in Haskell too. | |
-- For bonus fun, compare | |
-- > eddington [1,2,3] | |
-- with | |
-- > let l = [1,2,3] | |
-- > eddington l |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment