module CompositeSubstrings (check) where
primes :: (Integral a) => [a]
primes = 2 : ([3..] `minus` composites)
composites :: (Integral a) => [a]
composites = union $ map multiples primes
multiples :: (Integral a) => a -> [a]
multiples n = map (*n) [n..]
minus :: (Integral a) => [a] -> [a] -> [a]
(x:xs) `minus` (y:ys) | x < y = x : (xs `minus` (y:ys))
| x == y = xs `minus` ys
| x > y = (x:xs) `minus` ys
union :: (Integral a) => [[a]] -> [a]
union = foldr merge undefined
merge :: (Integral a) => [a] -> [a] -> [a]
merge (x:xs) ys = x : merge' xs ys
merge' :: (Integral a) => [a] -> [a] -> [a]
merge' (x:xs) (y:ys) | x < y = x : merge' xs (y:ys)
| x == y = x : merge' xs ys
| x > y = y : merge' (x:xs) ys
tails1 :: [a] -> [[a]]
tails1 = takeWhile (not . null) . iterate tail
inits1 :: [a] -> [[a]]
inits1 = foldr ( \ x z -> (x :) <$> [] : z ) []
isComposite n = foldr go undefined composites where
go composite z | composite > n = False
| composite == n = True
| otherwise = z
check :: Int -> Bool
check n = not $ any (isComposite . read) $ concatMap tails1 $ inits1 $ show n
module CompositeSubstringsSpec (spec) where
import CompositeSubstrings (check)
import Test.Hspec
import Data.Foldable (for_)
spec :: Spec
spec = do
it "click me open" $ do
for_ [1..99_999] $ \ n -> do
if check n then
print n
else
return ()
snoc
actually seems to make a faster list than cons
C = \ f x y . f y x
K = \ x _ . x
cons = \ x xs . \ c n . c x (xs c n)
nil = \ _c n . n
snoc = \ xs x . \ c n . xs c (c x n)
succ = \ n . \ s z . s (n s z)
xs = 15 2 (C snoc ()) nil
xs = 15 2 ( cons ()) nil
result = xs (K succ) 0
import { assert, LC, getSolution } from "./lc-test.js";
LC.configure({ purity: "LetRec", numEncoding: "Church", verbosity: "Concise" });
const { result } = LC.compile(getSolution());
describe("Test", () => {
it("example tests", () => {
assert.numEql( result, 32_768 );
});
});
Counting horses without phase alignment
function countHorses(sounds) {
function* horsify(leg) { for ( let phase=0;; yield [leg+phase,phase++] ); }
function filter(leg,phase) {
const sounds_ = Array.from(sounds);
for ( let i=leg-1-phase; i in sounds; i += leg )
if ( sounds[i] )
sounds_[i]--;
else
return null;
return sounds_;
}
const leg = 1 + sounds.findIndex(Boolean);
if ( leg )
for ( const horse of horsify(leg) ) {
const r = filter(...horse);
if ( r )
return [ horse, ...countHorses(r) ];
}
else
return [];
}
const {assert,config} = require("chai"); config.truncateThreshold = 0;
describe("Counting Horses", function() {
it("example tests", function() {
test( [0,0,0,0,0,0,0,0], [] );
test( [0,1,0,1,0,1,0,1,0], [[2,0]] );
test( [0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0], [[4,0]] );
test( [1,1,1,1,1], [[1,0]] );
test( [0,1,1,1,0,2,0,1,1,1], [[2,0], [3,0]] );
test( [0,2,1,2,0,3,0,2,1,2], [[2,0], [2,0], [3,0]] );
test( [1,2,2,2,1,3,1,2,2,2,1,3,1,2,2], [[1,0], [2,0], [3,0]] );
});
it("more example tests", function() {
test( [0,1,1,0,1,1,0,1,1,0,1,1], [[3,1],[3,0]] );
test( [0,1,1,0,0,1,0,1,0,1,0,0,1,1], [[4,2],[5,2]] );
test( [0,2,0,0,0,1,1,0,0,1,0,1,0,1], [[4,2],[5,3]] );
});
it("fixed tests", function() {
test( [1,0,0,0,2,0,1,0,2,2,2,1], [[4,3],[5,0],[7,0],[9,0],[10,0],[11,0],[11,0],[12,0]] );
test( [2,2,2,0,1,2,2,1,2,2,0,0], [[4,2],[4,2],[4,3],[5,2],[6,3],[6,5],[7,0]] );
test( [1,0,1,1,2,0,2,0,0,1,0,1], [[3,2],[7,0],[8,3],[8,3],[9,6]] );
test( [1,1,2,1,1,2,2,1,0,0,0,1], [[6,5],[5,3],[5,2],[10,7],[9,5],[8,3],[7,1],[7,1]] );
test( [1,2,1,2,0,0,0,1,2,2,0,0], [[7,6],[7,5],[7,5],[7,4],[6,2],[9,5]] );
test( [1,2,1,1,2,1,0,2,0,0,2,2], [[5,4],[3,1],[6,4],[9,6],[8,4],[8,3]] );
test( [2,0,0,0,1,2,2,0,0,2,2,2], [[5,4],[5,4],[5,0],[7,0],[7,0],[10,0],[12,0],[12,0]] );
test( [2,0,1,0,1,1,2,2,1,1,2,0], [[2,1],[5,4],[7,0],[8,0],[8,0],[10,0]] );
test( [0,2,0,0,1,0,0,0,0,1,2,0], [[8,6],[9,7],[6,1]] );
test( [1,0,2,1,2,2,0,1,2,0,2,0], [[4,3],[5,2],[6,3],[7,3],[6,1],[7,1],[7,1]] );
});
it("more fixed tests", function() {
test( [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], [[1,0]] );
test( [2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2], [[1,0],[3,0],[3,2]] );
test( [3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1,3], [[3,2],[2,1],[1,0]] );
test( [3,2,2,2,2,2,3,1,2,3,2,1,3,2,2,2,2,2,3,1,2,3,2,1,3,2,2,2,2,2,3,1,2,3,2,1,3,2,2,2,2,2,3,1,2,3,2,1,3], [[2,1],[1,0],[4,2],[3,2]] );
test( [2,4,2,3,3,3,2,4,2,3,3,3,2,4,2,3,3,3,2,4,2,3,3,3,2,4,2,3,3,3,2,4,2,3,3,3,2,4,2,3,3,3,2,4,2,3,3,3,2], [[3,1],[1,0],[1,0],[2,0]] );
test( [1,4,0,1,2,3,0,3,0,2,3,1,0,4,0,2,2,2,0,3,1,2,2,1,0,5,0,1,2,2,1,3,0,2,2,2,0,4,0,1,3,2,0,3,0,3,2,1,0], [[3,1],[3,1],[2,0],[5,4],[4,2]] );
test( [4,3,2,3,2,3,3,3,3,2,3,2,3,4,2,3,2,2,4,3,3,2,2,3,3,4,2,2,3,2,4,3,2,3,2,3,3,3,3,2,3,2,3,4,2,3,2,2,4], [[5,4],[6,5],[1,0],[5,1],[6,4],[1,0]] );
test( [2,3,2,4,2,3,2,4,2,3,2,4,2,3,2,4,2,3,2,4,2,3,2,4,2,3,2,4,2,3,2,4,2,3,2,4,2,3,2,4,2,3,2,4,2,3,2,4,2], [[1,0],[4,0],[1,0],[4,2],[4,0]] );
test( [2,2,1,5,3,2,2,2,3,2,3,3,2,4,1,2,3,3,2,3,3,2,2,3,3,2,1,4,4,2,1,3,2,3,3,3,2,2,3,2,3,3,1,4,2,3,2,2,4], [[2,0],[7,0],[7,3],[5,1],[4,3],[6,1],[1,0],[8,4]] );
test( [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], [[1,0]] );
test( [2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2,1,2,2], [[1,0],[3,0],[3,2]] );
test( [1,3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1,3,1,2,2,2,1], [[1,0],[2,0],[3,1]] );
test( [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3], [[1,0],[2,1],[2,0],[1,0]] );
test( [1,3,1,2,1,3,1,2,1,3,1,2,1,3,1,2,1,3,1,2,1,3,1,2,1,3,1,2,1,3,1,2,1,3,1,2,1,3,1,2,1,3,1,2,1,3,1,2,1], [[2,1],[2,0],[2,0],[4,2]] );
test( [3,1,2,3,2,2,2,1,4,2,1,2,3,2,2,2,2,2,3,1,3,2,1,3,3,1,2,2,3,2,2,1,3,3,1,2,3,1,3,2,2,2,2,2,3,2,1,2,4], [[4,3],[3,2],[3,0],[1,0],[5,1]] );
test( [3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3,4,4,3], [[2,0],[1,0],[2,1],[3,0],[1,0],[3,1]] );
test( [4,2,4,3,4,3,4,2,4,3,4,3,4,2,4,3,4,3,4,2,4,3,4,3,4,2,4,3,4,3,4,2,4,3,4,3,4,2,4,3,4,3,4,2,4,3,4,3,4], [[3,2],[4,3],[4,1],[6,1],[1,0],[1,0],[3,0]] );
test( [2,2,2,1,2,1,3,1,1,2,3,0,2,2,2,1,2,1,3,1,1,2,3,0,2,2,2,1,2,1,3,1,1,2,3,0,2,2,2,1,2,1,3,1,1,2,3,0,2], [[3,1],[4,1],[2,1],[4,2],[3,2]] );
test( [0,2,1,2,0,3,0,2,1,2,0,3,0,2,1,2,0,3,0,2,1,2,0,3,0,2,1,2,0,3,0,2,1,2,0,3,0,2,1,2,0,3,0,2,1,2,0,3,0], [[2,0],[3,0],[2,0]] );
test( [2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2], [[1,0],[2,1]] );
});
});
function fromHorses(horses,length) {
let r = Array(length).fill(0);
for ( const [leg,phase] of horses )
for ( let i=leg-1-phase; i in r; i+=leg )
r[i]++;
return r;
}
function test(sound,expected) {
const actual = countHorses([...sound]);
assert( Array.isArray(actual), `$ACTUAL is not an Array` );
assert( 0 <= actual.length && actual.length <= expected.length, `0 <= actual.length <= expected.length should hold` );
for ( const horse of actual )
assert( Array.isArray(horse), `$HORSE is not an Array` );
for ( const [leg,phase] of actual )
assert( Number.isInteger(leg) && Number.isInteger(phase), `$LEG or $PHASE is not an Integer` ),
assert( 0 < leg && leg <= sound.length, `0 < $LEG <= sound.length should hold` ),
assert( 0 <= phase && phase < leg, `0 <= $PHASE < $LEG should hold` );
assert.deepEqual( fromHorses(actual,sound.length), sound );
}
Counting horses without phase alignment
module CountingHorses (countHorses) where
import Data.List (findIndex)
import Data.Foldable (asum)
import Data.Maybe (fromMaybe)
countHorses :: [Int] -> [(Int,Int)]
countHorses sounds = fromMaybe [] $ ( \ leg -> asum $ ( \ horse -> (horse :) . countHorses <$> filter horse ) <$> [ (leg+phase,phase) | phase <- [0..] ] ) =<< succ <$> findIndex (/= 0) sounds where
filter (leg,phase) | all (>= 0) sounds' = Just sounds' | otherwise = Nothing where
sounds' = zipWith (-) sounds $ drop phase $ cycle $ replicate (leg-1) 0 ++ [1]
{-# Language BlockArguments #-}
module CountingHorsesSpec (spec) where
import CountingHorses (countHorses)
import Test.Hspec
import Data.Foldable (for_)
spec :: Spec
spec = do
it "example tests" $ do
test [0,0,0,0,0,0,0,0] []
test [0,1,0,1,0,1,0,1,0] [(2,0)]
test [0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0] [(4,0)]
test [1,1,1,1,1] [(1,0)]
test [0,1,1,1,0,2,0,1,1,1] [(2,0), (3,0)]
test [0,2,1,2,0,3,0,2,1,2] [(2,0), (2,0), (3,0)]
test [1,2,2,2,1,3,1,2,2,2,1,3,1,2,2] [(1,0), (2,0), (3,0)]
it "more example tests" $ do
test [0,1,1,0,1,1,0,1,1,0,1,1] [(3,0),(3,1)]
test [0,1,1,0,0,1,0,1,0,1,0,0,1,1] [(4,2),(5,2)]
test [0,2,0,0,0,1,1,0,0,1,0,1,0,1] [(4,2),(5,3)]
it "fixed tests" $ do
test [1,0,0,0,2,0,1,0,2,2,2,1] [(4,3),(5,0),(7,0),(9,0),(10,0),(11,0),(11,0),(12,0)]
test [2,2,2,0,1,2,2,1,2,2,0,0] [(4,2),(4,2),(4,3),(5,2),(6,3),(6,5),(7,0)]
test [1,0,1,1,2,0,2,0,0,1,0,1] [(3,2),(7,0),(8,3),(8,3),(9,6)]
test [1,1,2,1,1,2,2,1,0,0,0,1] [(6,5),(5,3),(5,2),(10,7),(9,5),(8,3),(7,1),(7,1)]
test [1,2,1,2,0,0,0,1,2,2,0,0] [(7,6),(7,5),(7,5),(7,4),(6,2),(9,5)]
test [1,2,1,1,2,1,0,2,0,0,2,2] [(5,4),(3,1),(6,4),(9,6),(8,4),(8,3)]
test [2,0,0,0,1,2,2,0,0,2,2,2] [(5,4),(5,4),(5,0),(7,0),(7,0),(10,0),(12,0),(12,0)]
test [2,0,1,0,1,1,2,2,1,1,2,0] [(2,1),(5,4),(7,0),(8,0),(8,0),(10,0)]
test [0,2,0,0,1,0,0,0,0,1,2,0] [(8,6),(9,7),(6,1)]
test [1,0,2,1,2,2,0,1,2,0,2,0] [(4,3),(5,2),(6,3),(7,3),(6,1),(7,1),(7,1)]
it "more fixed tests" $ do
test [2,1,2, 2,1,2, 2,1,2, 2,1,2, 2,1,2, 2,1,2, 2,1,2, 2,1,2, 2,1,2, 2,1,2, 2,1,2, 2,1,2, 2,1,2 ,2,1,2, 2,1,2, 2,1,2, 2]
[(1,0),(3,0),(3,2)]
test [3,1,2,2,2,1, 3,1,2,2,2,1, 3,1,2,2,2,1, 3,1,2,2,2,1, 3,1,2,2,2,1, 3,1,2,2,2,1, 3,1,2,2,2,1, 3,1,2,2,2,1, 3]
[(3,2),(2,1),(1,0)]
test [3,2,2,2,2,2, 3,1,2,3,2,1, 3,2,2,2,2,2, 3,1,2,3,2,1, 3,2,2,2,2,2, 3,1,2,3,2,1, 3,2,2,2,2,2, 3,1,2,3,2,1, 3]
[(2,1),(1,0),(4,2),(3,2)]
test [2,4,2,3,3,3, 2,4,2,3,3,3, 2,4,2,3,3,3, 2,4,2,3,3,3, 2,4,2,3,3,3, 2,4,2,3,3,3, 2,4,2,3,3,3, 2,4,2,3,3,3, 2]
[(3,1),(1,0),(1,0),(2,0)]
test [1,4,0,1,2,3,0,3,0,2,3,1,0,4,0,2,2,2,0,3,1,2,2,1,0,5,0,1,2,2,1,3,0,2,2,2,0,4,0,1,3,2,0,3,0,3,2,1,0]
[(3,1),(3,1),(2,0),(5,4),(4,2)]
test [4,3,2,3,2,3,3,3,3,2,3,2,3,4,2,3,2,2,4,3,3,2,2,3,3,4,2,2,3,2, 4,3,2,3,2,3,3,3,3,2,3,2,3,4,2,3,2,2,4]
[(5,4),(6,5),(1,0),(5,1),(6,4),(1,0)]
test [2,3,2,4, 2,3,2,4, 2,3,2,4, 2,3,2,4, 2,3,2,4, 2,3,2,4, 2,3,2,4, 2,3,2,4, 2,3,2,4, 2,3,2,4, 2,3,2,4, 2,3,2,4, 2]
[(1,0),(2,0),(1,0),(4,0)]
test [2,2,1,5,3,2,2,2,3,2,3,3,2,4,1,2,3,3,2,3,3,2,2,3,3,2,1,4,4,2,1,3,2,3,3,3,2,2,3,2,3,3,1,4,2,3,2,2,4]
[(2,0),(7,0),(7,3),(5,1),(4,3),(6,1),(1,0),(8,4)]
test [1,3,1, 2,2,2, 1,3,1, 2,2,2, 1,3,1, 2,2,2, 1,3,1, 2,2,2, 1,3,1, 2,2,2, 1,3,1, 2,2,2, 1,3,1, 2,2,2, 1,3,1, 2,2,2, 1]
[(1,0),(2,0),(3,1)]
test [1,3,1,2, 1,3,1,2, 1,3,1,2, 1,3,1,2, 1,3,1,2, 1,3,1,2, 1,3,1,2, 1,3,1,2, 1,3,1,2, 1,3,1,2, 1,3,1,2, 1,3,1,2, 1]
[(1,0),(2,0),(4,2)]
test [3,1,2,3,2,2,2,1,4,2,1,2,3,2,2,2,2,2,3,1,3,2,1,3,3,1,2,2,3,2,2,1,3,3,1,2,3,1,3,2,2,2,2,2,3,2,1,2,4]
[(4,3),(3,2),(3,0),(1,0),(5,1)]
test [3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3,4,4, 3]
[(1,0),(1,0),(3,0),(1,0),(3,1)]
test [4,2,4,3,4,3, 4,2,4,3,4,3, 4,2,4,3,4,3, 4,2,4,3,4,3, 4,2,4,3,4,3, 4,2,4,3,4,3, 4,2,4,3,4,3, 4,2,4,3,4,3, 4]
[(1,0),(1,0),(2,1),(2,1),(6,0),(6,2)]
test [2,2,2,1,2,1,3,1,1,2,3,0, 2,2,2,1,2,1,3,1,1,2,3,0, 2,2,2,1,2,1,3,1,1,2,3,0, 2,2,2,1,2,1,3,1,1,2,3,0, 2]
[(3,1),(4,1),(2,1),(4,2),(3,2)]
test [0,2,1,2,0,3, 0,2,1,2,0,3, 0,2,1,2,0,3, 0,2,1,2,0,3, 0,2,1,2,0,3, 0,2,1,2,0,3, 0,2,1,2,0,3, 0,2,1,2,0,3, 0]
[(2,0),(3,0),(2,0)]
test [2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2,1, 2]
[(1,0),(2,1)]
test :: [Int] -> [(Int,Int)] -> Expectation
test sound expected = do
let actual = countHorses sound
length actual `shouldSatisfy` (0 <=)
length actual `shouldSatisfy` (<= length expected)
for_ actual $ \ (leg,phase) -> do
leg `shouldSatisfy` (0 <)
leg `shouldSatisfy` (<= length sound)
phase `shouldSatisfy` (0 <=)
phase `shouldSatisfy` (< leg)
actual `shouldSatisfy` and . zipWith (==) sound . fromHorses
fromHorses :: [(Int,Int)] -> [Int]
fromHorses = foldl (zipWith (+)) (repeat 0)
. map \ (leg,phase) -> drop phase $ cycle $ replicate (leg-1) 0 ++ [1]
efficient implementation of an inefficient algorithm
module BubbleSort where
bubbleSort :: (Ord a) => [a] -> [a]
bubbleSort xs@(_:_:_) = a : bubbleSort as where
a:as = foldr bubble [] xs
bubble x [] = [x]
bubble x (z:zs) | x <= z = x : z : zs
| otherwise = z : x : zs
bubbleSort xs = xs
bubbleSortBy :: (a -> a -> Ordering) -> [a] -> [a]
bubbleSortBy cmp xs@(_:_:_) = a : bubbleSortBy cmp as where
a:as = foldr bubble [] xs
bubble x [] = [x]
bubble x (z:zs) | cmp x z /= GT = x : z : zs
| otherwise = z : x : zs
bubbleSortBy _ xs = xs
module BubbleSortSpec (spec) where
import BubbleSort
import Test.Hspec
import Test.QuickCheck
import Data.List
import Data.Function (on)
spec :: Spec
spec = do
it "random tests" $ do
mapSize (* 10) $ property $ \ xs -> do
bubbleSort xs `shouldBe` sort @Int xs
it "but is it stable?" $ do
mapSize (* 10) $ property $ \ xs -> do
let cmp = compare `on` fst
bubbleSortBy cmp xs `shouldBe` sortBy @(Int,Int) cmp xs
module Sum (Sum.sum) where import Control.Monad.State (State,execState,modify) import Data.Foldable (for_) sum :: [Int] -> Int sum xs = execState (for_ xs add) 0 where add :: Int -> State Int () add = modify . (+)
- module Sum (Sum.sum) where
import Control.Monad.State (State,execState,put, get)- import Control.Monad.State (State,execState,modify)
- import Data.Foldable (for_)
- sum :: [Int] -> Int
- sum xs = execState (for_ xs add) 0 where
- add :: Int -> State Int ()
add x = do-- modify (+x)y <- getput (y + x)return ()- add = modify . (+)
for_
: "Map each element of a structure to a monadic action, evaluate these actions from left to right, and ignore the results."
Evaluating the actions leads to a state that should hold the final result ( the sum ).
"Ignore the results" means add
should always end with return ()
.
module Sum (Sum.sum) where import Control.Monad.State (State,execState) import Data.Foldable (for_) sum :: [Int] -> Int sum xs = execState (for_ xs add) 0 where add :: Int -> State Int () add x = do return ()
- module Sum (Sum.sum) where
import Prelude hiding (sum)- import Control.Monad.State (State,execState)
- import Data.Foldable (for_)
sum :: [Word] -> Wordsum xs = if xs == [] then 0 else foldr1 (+) xs- sum :: [Int] -> Int
- sum xs = execState (for_ xs add) 0 where
- add :: Int -> State Int ()
- add x = do
- return ()
module SumSpec (spec) where import Prelude hiding (sum) import qualified Prelude as Pre (sum) import Sum (sum) import Test.Hspec import Test.QuickCheck spec :: Spec spec = do it "[] -> 0" $ do sum [] `shouldBe` Pre.sum [] it "[1] -> 1" $ do sum [1] `shouldBe` Pre.sum [1] it "[1,2,3] -> 6" $ do sum [1,2,3] `shouldBe` Pre.sum [1,2,3] it "random tests" $ do property $ \ xs -> do sum xs `shouldBe` Pre.sum xs
- module SumSpec (spec) where
- import Prelude hiding (sum)
- import qualified Prelude as Pre (sum)
- import Sum (sum)
- import Test.Hspec
- import Test.QuickCheck
- spec :: Spec
- spec = do
it "tests" $ doSum.sum [1] `shouldBe` Pre.sum [1]it "Empty list" $ doSum.sum [] `shouldBe` Pre.sum []- it "[] -> 0" $ do
- sum [] `shouldBe` Pre.sum []
- it "[1] -> 1" $ do
- sum [1] `shouldBe` Pre.sum [1]
- it "[1,2,3] -> 6" $ do
- sum [1,2,3] `shouldBe` Pre.sum [1,2,3]
- it "random tests" $ do
- property $ \ xs -> do
- sum xs `shouldBe` Pre.sum xs
sum
is again a oneliner, with a correct but flexible signature.
foldr
requires t
to be Foldable
, (+)
requires a
to be Num
, 0
is also Num
.
Explain that last test in the comments, or on Discord.
module Sum (Sum.sum) where import Prelude hiding (sum) sum :: (Foldable t,Num a) => t a -> a sum = foldr (+) 0
- module Sum (Sum.sum) where
- import Prelude hiding (sum)
sum :: (Foldable t) => t Word -> Wordsum xs | null xs = 0 | otherwise = foldr1 (+) xs- sum :: (Foldable t,Num a) => t a -> a
- sum = foldr (+) 0
module SumSpec (spec) where import Prelude hiding (sum) import qualified Prelude as Pre (sum) import Sum (sum) import Test.Hspec spec :: Spec spec = do it "tests" $ do Sum.sum [1] `shouldBe` Pre.sum [1] it "Empty list" $ do Sum.sum [] `shouldBe` Pre.sum [] it "Different container" $ do Sum.sum (Just 1) `shouldBe` Pre.sum (Just 1) it "negative numbers" $ do Sum.sum [-1] `shouldBe` Pre.sum [-1] it "big numbers" $ do Sum.sum [18446744073709551615] `shouldBe` Pre.sum [18446744073709551615] it "what happens here ?!?" $ do Sum.sum (1,2) `shouldBe` 2 -- and not 3
- module SumSpec (spec) where
- import Prelude hiding (sum)
- import qualified Prelude as Pre (sum)
- import Sum (sum)
- import Test.Hspec
- spec :: Spec
- spec = do
- it "tests" $ do
- Sum.sum [1] `shouldBe` Pre.sum [1]
- it "Empty list" $ do
- Sum.sum [] `shouldBe` Pre.sum []
- it "Different container" $ do
- Sum.sum (Just 1) `shouldBe` Pre.sum (Just 1)
- it "negative numbers" $ do
- Sum.sum [-1] `shouldBe` Pre.sum [-1]
- it "big numbers" $ do
Sum.sum [18446744073709551615] `shouldBe` Pre.sum [18446744073709551615]- Sum.sum [18446744073709551615] `shouldBe` Pre.sum [18446744073709551615]
- it "what happens here ?!?" $ do
- Sum.sum (1,2) `shouldBe` 2 -- and not 3
Get rid of the warnings.
module Sum (Sum.sum) where import Prelude hiding (sum) sum :: (Foldable t) => t Word -> Word sum xs | null xs = 0 | otherwise = foldr1 (+) xs
- module Sum (Sum.sum) where
- import Prelude hiding (sum)
sum :: Foldable t => t Word -> Word- sum :: (Foldable t) => t Word -> Word
- sum xs | null xs = 0 | otherwise = foldr1 (+) xs
module SumSpec (spec) where import Prelude hiding (sum) import qualified Prelude as Pre (sum) import Sum (sum) import Test.Hspec spec :: Spec spec = do it "tests" $ do Sum.sum [1] `shouldBe` Pre.sum [1] it "Empty list" $ do Sum.sum [] `shouldBe` Pre.sum [] it "Different container" $ do Sum.sum (Just 1) `shouldBe` Pre.sum (Just 1) it "negative numbers" $ do Sum.sum [-1] `shouldBe` Pre.sum [-1] it "big numbers" $ do Sum.sum [18446744073709551615] `shouldBe` Pre.sum [18446744073709551615]
- module SumSpec (spec) where
- import Prelude hiding (sum)
- import qualified Prelude as Pre (sum)
- import Sum (sum)
- import Test.Hspec
- spec :: Spec
- spec = do
- it "tests" $ do
- Sum.sum [1] `shouldBe` Pre.sum [1]
- it "Empty list" $ do
- Sum.sum [] `shouldBe` Pre.sum []
- it "Different container" $ do
Sum.sum (Just 1) `shouldBe` Pre.sum (Just 1)- Sum.sum (Just 1) `shouldBe` Pre.sum (Just 1)
- it "negative numbers" $ do
- Sum.sum [-1] `shouldBe` Pre.sum [-1]
- it "big numbers" $ do
- Sum.sum [18446744073709551615] `shouldBe` Pre.sum [18446744073709551615]
module Sum (Sum.sum) where import Prelude hiding (sum) sum :: [Word] -> Word sum xs | null xs = 0 | otherwise = foldr1 (+) xs
- module Sum (Sum.sum) where
- import Prelude hiding (sum)
- sum :: [Word] -> Word
sum xs = if xs == [] then 0 else foldr1 (+) xs- sum xs | null xs = 0 | otherwise = foldr1 (+) xs
module SumSpec (spec) where import Prelude hiding (sum) import qualified Prelude as Pre (sum) import Sum (sum) import Test.Hspec spec :: Spec spec = do it "tests" $ do Sum.sum [1] `shouldBe` Pre.sum [1] it "Empty list" $ do Sum.sum [] `shouldBe` Pre.sum [] it "Different container" $ do Sum.sum (Just 1) `shouldBe` Pre.sum (Just 1)
- module SumSpec (spec) where
- import Prelude hiding (sum)
- import qualified Prelude as Pre (sum)
- import Sum (sum)
- import Test.Hspec
- spec :: Spec
- spec = do
- it "tests" $ do
- Sum.sum [1] `shouldBe` Pre.sum [1]
- it "Empty list" $ do
Sum.sum [] `shouldBe` Pre.sum []- Sum.sum [] `shouldBe` Pre.sum []
- it "Different container" $ do
- Sum.sum (Just 1) `shouldBe` Pre.sum (Just 1)
module Sum (Sum.sum) where import Prelude hiding (sum) sum :: [Word] -> Word sum xs | null xs = 0 | otherwise = foldr1 (+) xs
- module Sum (Sum.sum) where
- import Prelude hiding (sum)
- sum :: [Word] -> Word
sum xs = if xs == [] then 0 else foldr1 (+) xs- sum xs | null xs = 0 | otherwise = foldr1 (+) xs
Rules
https://discord.com/channels/846624424199061524/1204166960909127731/1204166965271207936
Task
add all the numbers in the input. return this sum.
Restrictions
Do not use Prelude.sum
.
I might, later, introduce frameworks ( recursion combinators, monoids, monads ) to work within.
module Sum (Sum.sum) where
import Prelude hiding (sum)
sum :: [Word] -> Word
sum = foldr1 (+)
module SumSpec (spec) where
import Prelude hiding (sum)
import qualified Prelude as Pre (sum)
import Sum (sum)
import Test.Hspec
spec :: Spec
spec = do
it "tests" $ do
Sum.sum [1] `shouldBe` Pre.sum [1]
#debug
#import combinators.lc
B = \ f g x . f (g x)
I = \ x . x
K = \ x _ . x
T = \ x f . f x
#import binary-scott-number.lc
# 0 = \ end _even _odd . end
Bit0 = \ n . \ _end even _odd . even n
Bit1 = \ n . \ _end _even odd . odd n
shift = \ n . n 0 I I
dbl = \ n . n 0 (K (Bit0 n)) (K (Bit0 n))
succ = \ n . n 1 Bit1 (B Bit0 succ)
pred = \ n . n 0 (B Bit1 pred) dbl
add = \ m n . m n
( \ zm . n m (B Bit0 (add zm)) (B Bit1 (add zm)) )
( \ zm . n m (B Bit1 (add zm)) (B Bit0 (B succ (add zm))) )
mul = \ m n . m 0
( \ zm . n 0
( \ zn . Bit0 (Bit0 (mul zm zn)) )
( \ _z . Bit0 (mul zm n) )
)
( \ zm . n 0
( \ zn . Bit0 (mul m zn) )
( \ zn . Bit1 (add (dbl (mul zm zn)) (add zm zn)) )
)
#import scott-tree.lc
Tree = \ x left right . \ tree . tree x left right # Tree = Tree Number Tree Tree
map = \ fn tree . tree \ x left right . Tree (fn x) (map fn left) (map fn right)
index = \ tree i . tree \ x left right . i x (B (index right) pred) (index left)
tree = Tree 1 (map Bit0 tree) (map Bit1 tree)
memo = \ fn . index (map (B fn pred) tree)
# fibonacci :: Number -> Number
fibonacci = memo \ n .
T (shift (succ n)) \ n' .
T (fibonacci n') \ fn .
T (fibonacci (pred n')) \ fn' .
n 0 # n == 0
(K (mul fn (add fn (Bit0 fn')))) # even n
( \ z . z 1 # n == 1
(K (add (mul fn fn) (mul fn' fn'))) # odd n
(K (add (mul fn fn) (mul fn' fn'))) # odd n
)
import { assert, LC, getSolution } from "./lc-test.js";
LC.configure({ purity: "LetRec", numEncoding: "BinaryScott", verbosity: "Calm" });
const { fibonacci: usrFibonacci } = LC.compile(getSolution());
const toBigInt = n => n (0n) ( n => 2n * toBigInt(n) ) ( n => 1n + 2n * toBigInt(n) ) ;
const fibonacci = n => toBigInt(usrFibonacci(n)) ;
const refFibonacci = n => function go(n,a,b) { return n ? go(n-1,b,a+b) : a ; } ( n, 0n, 1n ) ;
describe("Fibonacci", () => {
it("smaller example tests", () => {
assert.equal( fibonacci(0), 0n );
assert.equal( fibonacci(1), 1n );
assert.equal( fibonacci(2), 1n );
assert.equal( fibonacci(3), 2n );
assert.equal( fibonacci(4), 3n );
assert.equal( fibonacci(5), 5n );
});
it("larger example tests", () => {
assert.equal( fibonacci(10), 55n );
assert.equal( fibonacci(20), 6765n );
assert.equal( fibonacci(30), 832040n );
assert.equal( fibonacci(40), 102334155n );
assert.equal( fibonacci(50), 12586269025n );
});
it("huge example test", () => { // takes 7½ seconds on a good day, 10 on a bad
// assert.equal( fibonacci(800), refFibonacci(800) );
assert.equal( fibonacci(800), 69283081864224717136290077681328518273399124385204820718966040597691435587278383112277161967532530675374170857404743017623467220361778016172106855838975759985190398725n ); // 555 bits
});
});
function dijkstra(tolls,[y0,x0],[yn,xn]) { const WIDTH = tolls[0].length; tolls = tolls.flat(); const pos0 = y0 * WIDTH + x0, posN = yn * WIDTH + xn; const costs = new Map(tolls.map( (_,i) => [i,Infinity] )).set(pos0,0); const poss = new Heap([ new Entry(0,pos0) ]); const res = new Map; while ( poss.size ) { let { priority: cost, payload: pos } = poss.minView(); if ( pos===posN ) poss.clear(); else if ( Number.isFinite(costs.get(pos)) ) { costs.delete(pos); cost += tolls[pos]; for ( const [pos1,dir] of [ [[pos-WIDTH,Up ], [pos+WIDTH,Down ]] , pos%WIDTH ? [[pos- 1,Left ]] : [] , pos%WIDTH+1-WIDTH ? [[pos+ 1,Right]] : [] ].flat().filter( ([pos]) => costs.has(pos) ) ) if ( cost < costs.get(pos1) ) costs.set(pos1,cost), poss.insert(new Entry(cost,pos1)), res.set(pos1,dir); } } return function massage(pos) { return res.get(pos)===Up ? [ ...massage(pos+WIDTH), Up ] : res.get(pos)===Down ? [ ...massage(pos-WIDTH), Down ] : res.get(pos)===Left ? [ ...massage(pos+1), Left ] : res.get(pos)===Right ? [ ...massage(pos-1), Right ] : [] ; } ( posN ) ; }
function path(tolls,[y0,x0],[yn,xn]) {- function dijkstra(tolls,[y0,x0],[yn,xn]) {
- const WIDTH = tolls[0].length;
- tolls = tolls.flat();
- const pos0 = y0 * WIDTH + x0, posN = yn * WIDTH + xn;
- const costs = new Map(tolls.map( (_,i) => [i,Infinity] )).set(pos0,0);
- const poss = new Heap([ new Entry(0,pos0) ]);
- const res = new Map;
while ( poss.length ) {- while ( poss.size ) {
- let { priority: cost, payload: pos } = poss.minView();
- if ( pos===posN )
poss.length = 0;- poss.clear();
- else if ( Number.isFinite(costs.get(pos)) ) {
- costs.delete(pos);
- cost += tolls[pos];
- for ( const [pos1,dir] of [ [[pos-WIDTH,Up ],
- [pos+WIDTH,Down ]] ,
- pos%WIDTH ? [[pos- 1,Left ]] : [] ,
- pos%WIDTH+1-WIDTH ? [[pos+ 1,Right]] : [] ].flat().filter( ([pos]) => costs.has(pos) ) )
- if ( cost < costs.get(pos1) )
- costs.set(pos1,cost),
- poss.insert(new Entry(cost,pos1)),
- res.set(pos1,dir);
- }
- }
- return function massage(pos) {
- return res.get(pos)===Up ? [ ...massage(pos+WIDTH), Up ] :
- res.get(pos)===Down ? [ ...massage(pos-WIDTH), Down ] :
- res.get(pos)===Left ? [ ...massage(pos+1), Left ] :
- res.get(pos)===Right ? [ ...massage(pos-1), Right ] :
- [] ;
- } ( posN ) ;
- }
const {assert,config} = require("chai"); config.truncateThreshold = 0; const rnd = (m,n=0) => Math.random() * (n-m) + m | 0 ; const rndGrid = length => Array.from( { length }, () => Array.from( { length }, () => rnd(0,10) ) ) ; const walk = path => function(tolls,[y0,x0],[yn,xn]) { const [cost,y,x] = path.reduce( ([cost,y,x],dir) => { assert.isAtLeast(y,0,"y coordinate"); assert.isAtLeast(x,0,"x coordinate"); assert.isBelow(y,tolls.length, "y coordinate"); assert.isBelow(x,tolls[0].length, "x coordinate"); return [ cost + tolls[y][x] , y + (dir===Down) - (dir===Up) , x + (dir===Right) - (dir===Left) ]; }, [0,y0,x0] ); assert.strictEqual( y, yn ); assert.strictEqual( x, xn ); return cost; } ; function floodfill(tolls,[y0,x0],[yn,xn]) { const WIDTH = tolls[0].length; tolls = tolls.flat(); const pos0 = y0 * WIDTH + x0, posN = yn * WIDTH + xn; const costs = tolls.map( () => Infinity ); costs[pos0] = 0; const res = new Map(); const work = [ pos0 ]; while ( work.length ) { const pos = work.shift(), cost = costs[pos], toll = tolls[pos]; for ( const [pos1,dir] of [ pos >= WIDTH ? [ [ pos-WIDTH, Up ] ] : [] , pos+WIDTH in tolls ? [ [ pos+WIDTH, Down ] ] : [] , pos%WIDTH ? [ [ pos-1, Left ] ] : [] , pos%WIDTH+1-WIDTH ? [ [ pos+1, Right ] ] : [] ].flat() ) if ( cost + toll < costs[pos1] ) costs[pos1] = cost + toll, res.set(pos1,dir), work.push(pos1); } return function massage(pos) { return res.get(pos)===Up ? [ ...massage(pos+WIDTH), Up ] : res.get(pos)===Down ? [ ...massage(pos-WIDTH), Down ] : res.get(pos)===Left ? [ ...massage(pos+1), Left ] : res.get(pos)===Right ? [ ...massage(pos-1), Right ] : [] ; } ( posN ) ; } describe("Cheapest Path", function() { it("walk around the ridge", function() { assert.deepEqual( dijkstra( [[1,9,1],[1,9,1],[1,1,1]], [0,0], [0,2] ), [ Down, Down, Right, Right, Up, Up ] ); }); it("walk over the ridge", function() { assert.deepEqual( dijkstra( [[1,3,1],[1,3,1],[1,1,1]], [0,0], [0,2] ), [ Right, Right ] ); }); it("randoms", function() { const SIZE = 100; for ( let i=1; i<=10; i++ ) { const tolls = rndGrid(SIZE); //*/ { const expectedPath = floodfill (tolls,[0,0],[0,SIZE-1]); const actualPath = dijkstra (tolls,[0,0],[0,SIZE-1]); const expectedCost = walk(expectedPath)(tolls,[0,0],[0,SIZE-1]); const actualCost = walk (actualPath)(tolls,[0,0],[0,SIZE-1]); assert.equal(actualCost,expectedCost,"path cost"); } //*/ { const expectedPath = floodfill (tolls,[0,0],[SIZE-1,SIZE-1]); const actualPath = dijkstra (tolls,[0,0],[SIZE-1,SIZE-1]); const expectedCost = walk(expectedPath)(tolls,[0,0],[SIZE-1,SIZE-1]); const actualCost = walk (actualPath)(tolls,[0,0],[SIZE-1,SIZE-1]); assert.equal(actualCost,expectedCost,"path cost"); } //*/ } }); });
- const {assert,config} = require("chai"); config.truncateThreshold = 0;
- const rnd = (m,n=0) => Math.random() * (n-m) + m | 0 ;
- const rndGrid = length => Array.from( { length }, () => Array.from( { length }, () => rnd(0,10) ) ) ;
- const walk = path => function(tolls,[y0,x0],[yn,xn]) {
- const [cost,y,x] = path.reduce( ([cost,y,x],dir) => {
- assert.isAtLeast(y,0,"y coordinate");
- assert.isAtLeast(x,0,"x coordinate");
- assert.isBelow(y,tolls.length, "y coordinate");
- assert.isBelow(x,tolls[0].length, "x coordinate");
- return [ cost + tolls[y][x] , y + (dir===Down) - (dir===Up) , x + (dir===Right) - (dir===Left) ];
- }, [0,y0,x0] );
- assert.strictEqual( y, yn );
- assert.strictEqual( x, xn );
- return cost;
- } ;
function refPath(tolls,[y0,x0],[yn,xn]) {- function floodfill(tolls,[y0,x0],[yn,xn]) {
- const WIDTH = tolls[0].length;
- tolls = tolls.flat();
- const pos0 = y0 * WIDTH + x0, posN = yn * WIDTH + xn;
- const costs = tolls.map( () => Infinity ); costs[pos0] = 0;
- const res = new Map();
- const work = [ pos0 ];
- while ( work.length ) {
- const pos = work.shift(), cost = costs[pos], toll = tolls[pos];
- for ( const [pos1,dir] of [ pos >= WIDTH ? [ [ pos-WIDTH, Up ] ] : [] ,
- pos+WIDTH in tolls ? [ [ pos+WIDTH, Down ] ] : [] ,
- pos%WIDTH ? [ [ pos-1, Left ] ] : [] ,
- pos%WIDTH+1-WIDTH ? [ [ pos+1, Right ] ] : [] ].flat() )
- if ( cost + toll < costs[pos1] )
- costs[pos1] = cost + toll,
- res.set(pos1,dir),
- work.push(pos1);
- }
- return function massage(pos) {
- return res.get(pos)===Up ? [ ...massage(pos+WIDTH), Up ] :
- res.get(pos)===Down ? [ ...massage(pos-WIDTH), Down ] :
- res.get(pos)===Left ? [ ...massage(pos+1), Left ] :
- res.get(pos)===Right ? [ ...massage(pos-1), Right ] :
- [] ;
- } ( posN ) ;
- }
- describe("Cheapest Path", function() {
- it("walk around the ridge", function() {
assert.deepEqual( path( [[1,9,1],[1,9,1],[1,1,1]], [0,0], [0,2] ), [ Down, Down, Right, Right, Up, Up ] );- assert.deepEqual( dijkstra( [[1,9,1],[1,9,1],[1,1,1]], [0,0], [0,2] ), [ Down, Down, Right, Right, Up, Up ] );
- });
- it("walk over the ridge", function() {
assert.deepEqual( path( [[1,4,1],[1,4,1],[1,1,1]], [0,0], [0,2] ), [ Right, Right ] );- assert.deepEqual( dijkstra( [[1,3,1],[1,3,1],[1,1,1]], [0,0], [0,2] ), [ Right, Right ] );
- });
- it("randoms", function() {
- const SIZE = 100;
- for ( let i=1; i<=10; i++ ) {
- const tolls = rndGrid(SIZE);
const expectedPath = refPath (tolls,[0,0],[0,SIZE-1]);const actualPath = path (tolls,[0,0],[0,SIZE-1]);const expectedCost = walk(expectedPath)(tolls,[0,0],[0,SIZE-1]);const actualCost = walk (actualPath)(tolls,[0,0],[0,SIZE-1]);assert.equal(actualCost,expectedCost,"path cost");- //*/
- { const expectedPath = floodfill (tolls,[0,0],[0,SIZE-1]);
- const actualPath = dijkstra (tolls,[0,0],[0,SIZE-1]);
- const expectedCost = walk(expectedPath)(tolls,[0,0],[0,SIZE-1]);
- const actualCost = walk (actualPath)(tolls,[0,0],[0,SIZE-1]);
- assert.equal(actualCost,expectedCost,"path cost");
- }
- //*/
- { const expectedPath = floodfill (tolls,[0,0],[SIZE-1,SIZE-1]);
- const actualPath = dijkstra (tolls,[0,0],[SIZE-1,SIZE-1]);
- const expectedCost = walk(expectedPath)(tolls,[0,0],[SIZE-1,SIZE-1]);
- const actualCost = walk (actualPath)(tolls,[0,0],[SIZE-1,SIZE-1]);
- assert.equal(actualCost,expectedCost,"path cost");
- }
- //*/
- }
- });
- });