module Example (TicTacToe,empty,move) where
import Data.Set as Set (Set,fromList,toList)
import Data.List (elemIndices)
type TicTacToe = [Word] -- 9 × 0..2
type Player = Word
empty :: TicTacToe
empty = replicate 9 0
won :: Player -> TicTacToe -> Bool
won player board = any (all ((==) player . (!!) board))
[ [0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6] ]
move :: Player -> Set TicTacToe -> Set TicTacToe
move player boards = Set.fromList $ concatMap move $ filter (not . won opponent) $ Set.toList boards where
opponent | player == 1 = 2 | otherwise = 1
move board = map ( \ p -> update p player board ) $ elemIndices 0 $ board
update :: Int -> Player -> TicTacToe -> TicTacToe
update p player board = left ++ player : right where (left,_:right) = splitAt p board
module ExampleSpec (spec) where
import Example (TicTacToe,empty,move)
import Test.Hspec
import Data.Set as Set (Set,singleton)
import Data.Foldable (for_)
import Data.List.Split (chunksOf)
toInt :: [Word] -> Word
toInt = foldl ((+) . (3 *)) 0
display :: Set TicTacToe -> IO ()
display boards = do
for_ boards $ \ board -> do
putStr $ show (toInt board) ++ ","
spec :: Spec
spec = do
it "tests" $ do
let boards0 = Set.singleton empty
display boards0
let boards1 = move 1 boards0
display boards1
let boards2 = move 2 boards1
display boards2
let boards3 = move 1 boards2
display boards3
let boards4 = move 2 boards3
display boards4
let boards5 = move 1 boards4
display boards5
let boards6 = move 2 boards5
display boards6
let boards7 = move 1 boards6
display boards7
let boards8 = move 2 boards7
display boards8
let boards9 = move 1 boards8
display boards9
module Example (fib) where fibonacci :: [Integer] fibonacci = 0 : scanl (+) 1 fibonacci fib :: Int -> Integer fib = (!!) fibonacci
module Example (fib,fibonacci) wheretype RAList a = [ ( Int, Tree a ) ]data Tree a = Empty | Node { here :: a, left, right :: Tree a } deriving ()nil :: RAList anil = []cons :: a -> RAList a -> RAList acons x ( (size0,tree0) : (size1,tree1) : trees ) | size0 == size1 = ( 1 + size0 + size1, Node x tree0 tree1 ) : treescons x trees = (1,Node x Empty Empty) : treesfromListN :: Int -> [a] -> RAList afromListN n = foldr cons nil . take n(!) :: RAList a -> Int -> a( (size,tree) : trees ) ! n | n < size = index tree size n | otherwise = trees ! (n - size) whereindex tree size n | n == 0 = here tree| n+n < size = index (left tree) (size `div` 2) (n-1)| otherwise = index (right tree) (size `div` 2) (n-1 - size `div` 2)- module Example (fib) where
- fibonacci :: [Integer]
- fibonacci = 0 : scanl (+) 1 fibonacci
- fib :: Int -> Integer
fib = (!) (fromListN 200_002 fibonacci)- fib = (!!) fibonacci
module ExampleSpec (spec) where import Example (fib) import Test.Hspec import Data.Ratio ((%),numerator) spec :: Spec spec = do it "tests" $ do fib 0 `shouldBe` 0 fib 1 `shouldBe` 1 fib 2 `shouldBe` 1 fib 3 `shouldBe` 2 fib 4 `shouldBe` 3 fib 5 `shouldBe` 5 fib 6 `shouldBe` 8 fib 7 `shouldBe` 13 fib 8 `shouldBe` 21 it "fib 25 000" $ do fib 25_000 `shouldBe` refFib 25_000 it "fib 50 000" $ do fib 50_000 `shouldBe` refFib 50_000 it "fib 100 000" $ do fib 100_000 `shouldBe` refFib 100_000 it "fib 200 000" $ do fib 200_000 `shouldBe` refFib 200_000 it "fib 200 001" $ do fib 200_001 `shouldBe` refFib 200_001 it "fib 120 002" $ do fib 120_002 `shouldBe` refFib 120_002 data Sqrt5 a = Sqrt5 { real, int :: a } instance (Num a) => Num (Sqrt5 a) where Sqrt5 a b + Sqrt5 c d = Sqrt5 (a+c) (b+d) Sqrt5 a b - Sqrt5 c d = Sqrt5 (a-c) (b-d) Sqrt5 a b * Sqrt5 c d = Sqrt5 (a*d+b*c) (5*a*c+b*d) fromInteger = Sqrt5 0 . fromInteger abs = undefined signum = undefined instance (Fractional a) => Fractional (Sqrt5 a) where recip (Sqrt5 a b) = Sqrt5 (a / (5*a*a-b*b)) (- b / (5*a*a-b*b)) fromRational = Sqrt5 0 . fromRational phi,sqrt5 :: Sqrt5 Rational phi = Sqrt5 (1%2) (1%2) sqrt5 = Sqrt5 1 0 refFib :: Integer -> Integer refFib n = numerator . int $ (phi^^n - (-phi)^^(-n)) / sqrt5
- module ExampleSpec (spec) where
import Example (fib,fibonacci)- import Example (fib)
- import Test.Hspec
- import Data.Ratio ((%),numerator)
- spec :: Spec
- spec = do
- it "tests" $ do
- fib 0 `shouldBe` 0
- fib 1 `shouldBe` 1
- fib 2 `shouldBe` 1
- fib 3 `shouldBe` 2
- fib 4 `shouldBe` 3
- fib 5 `shouldBe` 5
- fib 6 `shouldBe` 8
- fib 7 `shouldBe` 13
- fib 8 `shouldBe` 21
- it "fib 25 000" $ do
- fib 25_000 `shouldBe` refFib 25_000
- it "fib 50 000" $ do
- fib 50_000 `shouldBe` refFib 50_000
- it "fib 100 000" $ do
- fib 100_000 `shouldBe` refFib 100_000
- it "fib 200 000" $ do
- fib 200_000 `shouldBe` refFib 200_000
- it "fib 200 001" $ do
- fib 200_001 `shouldBe` refFib 200_001
- it "fib 120 002" $ do
- fib 120_002 `shouldBe` refFib 120_002
- data Sqrt5 a = Sqrt5 { real, int :: a }
- instance (Num a) => Num (Sqrt5 a) where
- Sqrt5 a b + Sqrt5 c d = Sqrt5 (a+c) (b+d)
- Sqrt5 a b - Sqrt5 c d = Sqrt5 (a-c) (b-d)
- Sqrt5 a b * Sqrt5 c d = Sqrt5 (a*d+b*c) (5*a*c+b*d)
- fromInteger = Sqrt5 0 . fromInteger
- abs = undefined
- signum = undefined
- instance (Fractional a) => Fractional (Sqrt5 a) where
- recip (Sqrt5 a b) = Sqrt5 (a / (5*a*a-b*b)) (- b / (5*a*a-b*b))
- fromRational = Sqrt5 0 . fromRational
- phi,sqrt5 :: Sqrt5 Rational
- phi = Sqrt5 (1%2) (1%2)
- sqrt5 = Sqrt5 1 0
- refFib :: Integer -> Integer
- refFib n = numerator . int $ (phi^^n - (-phi)^^(-n)) / sqrt5
module Example (fib,fibonacci) where type RAList a = [ ( Int, Tree a ) ] data Tree a = Empty | Node { here :: a, left, right :: Tree a } deriving () nil :: RAList a nil = [] cons :: a -> RAList a -> RAList a cons x ( (size0,tree0) : (size1,tree1) : trees ) | size0 == size1 = ( 1 + size0 + size1, Node x tree0 tree1 ) : trees cons x trees = (1,Node x Empty Empty) : trees fromListN :: Int -> [a] -> RAList a fromListN n = foldr cons nil . take n (!) :: RAList a -> Int -> a ( (size,tree) : trees ) ! n | n < size = index tree size n | otherwise = trees ! (n - size) where index tree size n | n == 0 = here tree | n+n < size = index (left tree) (size `div` 2) (n-1) | otherwise = index (right tree) (size `div` 2) (n-1 - size `div` 2) fibonacci :: [Integer] fibonacci = 0 : scanl (+) 1 fibonacci fib :: Int -> Integer fib = (!) (fromListN 200_002 fibonacci)
- module Example (fib,fibonacci) where
import Data.Bifunctor (bimap)- type RAList a = [ ( Int, Tree a ) ]
- data Tree a = Empty | Node { here :: a, left, right :: Tree a } deriving ()
data Tree a = Node { here :: a, left, right :: Tree a } deriving ()- nil :: RAList a
- nil = []
fromList :: [a] -> Tree afromList = head . foldr build undefined . rows 1 whererows l xs = uncurry (:) $ bimap (l,) (rows $ 2*l) $ splitAt l xsbuild (l,xs) zs = uncurry (zipWith3 Node xs) $ splitAt l zszipWith3 fn (x:xs) ~(y:ys) ~(z:zs) = fn x y z : zipWith3 fn xs ys zszipWith3 _ _ _ _ = [](!) :: Tree a -> Int -> atree ! 0 = here treetree ! n | odd n = left tree ! (n `div` 2) | otherwise = right tree ! (pred n `div` 2)- cons :: a -> RAList a -> RAList a
- cons x ( (size0,tree0) : (size1,tree1) : trees ) | size0 == size1 = ( 1 + size0 + size1, Node x tree0 tree1 ) : trees
- cons x trees = (1,Node x Empty Empty) : trees
- fromListN :: Int -> [a] -> RAList a
- fromListN n = foldr cons nil . take n
- (!) :: RAList a -> Int -> a
- ( (size,tree) : trees ) ! n | n < size = index tree size n | otherwise = trees ! (n - size) where
- index tree size n | n == 0 = here tree
- | n+n < size = index (left tree) (size `div` 2) (n-1)
- | otherwise = index (right tree) (size `div` 2) (n-1 - size `div` 2)
- fibonacci :: [Integer]
- fibonacci = 0 : scanl (+) 1 fibonacci
- fib :: Int -> Integer
fib = (!) (fromList fibonacci)- fib = (!) (fromListN 200_002 fibonacci)
module Example (fib,fibonacci) where
import Data.Bifunctor (bimap)
data Tree a = Node { here :: a, left, right :: Tree a } deriving ()
fromList :: [a] -> Tree a
fromList = head . foldr build undefined . rows 1 where
rows l xs = uncurry (:) $ bimap (l,) (rows $ 2*l) $ splitAt l xs
build (l,xs) zs = uncurry (zipWith3 Node xs) $ splitAt l zs
zipWith3 fn (x:xs) ~(y:ys) ~(z:zs) = fn x y z : zipWith3 fn xs ys zs
zipWith3 _ _ _ _ = []
(!) :: Tree a -> Int -> a
tree ! 0 = here tree
tree ! n | odd n = left tree ! (n `div` 2) | otherwise = right tree ! (pred n `div` 2)
fibonacci :: [Integer]
fibonacci = 0 : scanl (+) 1 fibonacci
fib :: Int -> Integer
fib = (!) (fromList fibonacci)
module ExampleSpec (spec) where
import Example (fib,fibonacci)
import Test.Hspec
import Data.Ratio ((%),numerator)
spec :: Spec
spec = do
it "tests" $ do
fib 0 `shouldBe` 0
fib 1 `shouldBe` 1
fib 2 `shouldBe` 1
fib 3 `shouldBe` 2
fib 4 `shouldBe` 3
fib 5 `shouldBe` 5
fib 6 `shouldBe` 8
fib 7 `shouldBe` 13
fib 8 `shouldBe` 21
it "fib 25 000" $ do
fib 25_000 `shouldBe` refFib 25_000
it "fib 50 000" $ do
fib 50_000 `shouldBe` refFib 50_000
it "fib 100 000" $ do
fib 100_000 `shouldBe` refFib 100_000
it "fib 200 000" $ do
fib 200_000 `shouldBe` refFib 200_000
it "fib 200 001" $ do
fib 200_001 `shouldBe` refFib 200_001
it "fib 120 002" $ do
fib 120_002 `shouldBe` refFib 120_002
data Sqrt5 a = Sqrt5 { real, int :: a }
instance (Num a) => Num (Sqrt5 a) where
Sqrt5 a b + Sqrt5 c d = Sqrt5 (a+c) (b+d)
Sqrt5 a b - Sqrt5 c d = Sqrt5 (a-c) (b-d)
Sqrt5 a b * Sqrt5 c d = Sqrt5 (a*d+b*c) (5*a*c+b*d)
fromInteger = Sqrt5 0 . fromInteger
abs = undefined
signum = undefined
instance (Fractional a) => Fractional (Sqrt5 a) where
recip (Sqrt5 a b) = Sqrt5 (a / (5*a*a-b*b)) (- b / (5*a*a-b*b))
fromRational = Sqrt5 0 . fromRational
phi,sqrt5 :: Sqrt5 Rational
phi = Sqrt5 (1%2) (1%2)
sqrt5 = Sqrt5 1 0
refFib :: Integer -> Integer
refFib n = numerator . int $ (phi^^n - (-phi)^^(-n)) / sqrt5
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