Code
Diff
  • public static class ReverseFactorial  {
      public static int Kata(long n) {
        long i = 1;
        while ( n > 1 ) n /= i += 1 ;
        return (int) i;
      }
    }
  • 1
    using System;
    
    2
    3
        public static class ReverseFactorial
    
    4
        {
    
    5
            public static int Kata(long n)
    
    6
            {
    
    7
              if(n == 0) return 1;
    
    8
              for (long i = 2; n / i > 1; i++)
    
    9
              {
    
    10
                n /= i;
    
    11
              }
    
    12
              return (int) n;
    
    13
            }
    
    14
        }
    
    1+
    public static class ReverseFactorial  {
    
    2+
      public static int Kata(long n) {
    
    3+
        long i = 1;
    
    4+
        while ( n > 1 ) n /= i += 1 ;
    
    5+
        return (int) i;
    
    6+
      }
    
    7+
    }
    

custom.map and custom.filter should return custom arrays, not ordinary ones.

wrapper can be used for random tests as well.

Code
Diff
  • // this code should fail the tests // it does
    
    function totalAgeYoungerThan0(people, age) {
      let totalAge = 0;
      for ( let i=0; i in people; i++ ) // this will just always return 0 with the arguments we send
        if ( people[i].age < age )
          totalAge += people[i].age;
      return totalAge;
    }
    
    // this code should fail the tests // it does
    
    function totalAgeYoungerThan1(people,tooOld) {
      let totalAge = 0;
      for ( const {age} of people ) // this will throw because our argument isn't iterable
        if ( age < tooOld )
          totalAge += age;
      return totalAge;
    }
    
    // this code should fail the tests // it does
    
    function totalAgeYoungerThan2(xs, age) {
      const [person,...persons] = xs; // this will throw on not iterable
      if ( person===undefined )
        return 0;
      else if ( person.age < age )
        return totalAgeYoungerThan2(persons,age) + person.age;
      else
        return totalAgeYoungerThan2(persons,age);
    }
    
    // this code should theoretically pass the tests // it does
    // it includes the word `for` but not in a for loop
    
    function totalAgeYoungerThan3(people, age) {
      return people
        .filter(person => person.age < age)
        .reduce((formula, person) => formula + person.age, 0)
    }
    
    // passes
    
    function totalAgeYoungerThan4(people, age) {
      return people
        .filter(person => person.age < age)
        .reduce((sum, person) => sum + person.age, 0)
    }
    
    // passes
    
    const get = i => o => o[i] ;
    const lt = x => y => y < x ;
    const plus = (x,y) => y+x ;
    
    function totalAgeYoungerThan5(people,age) {
      return people.map(get("age"))
                   .filter(lt(age))
                   .reduce(plus,0)
                   ;
    }
  • 4343
    4444
    function totalAgeYoungerThan4(people, age) {
    
    4545
      return people
    
    4646
        .filter(person => person.age < age)
    
    4747
        .reduce((sum, person) => sum + person.age, 0)
    
    4848
    }
    
    49+
    50+
    // passes
    
    51+
    52+
    const get = i => o => o[i] ;
    
    53+
    const lt = x => y => y < x ;
    
    54+
    const plus = (x,y) => y+x ;
    
    55+
    56+
    function totalAgeYoungerThan5(people,age) {
    
    57+
      return people.map(get("age"))
    
    58+
                   .filter(lt(age))
    
    59+
                   .reduce(plus,0)
    
    60+
                   ;
    
    61+
    }
    

The second fixed tests does not pass people but [], so define empty for that.

The random tests still pass regular arrays, so they always succeed. Set the number to 0 for now.

Removed the second function. Let's get this working first.

You can define multiple functions with the same name. Last one counts. If you want an earlier function, just give all later functions a different name. Could also number them, and define a single function to test at the top of the tests. That way, you have to make only one change. OK, I did that.

Code
Diff
  • // this code should fail the tests // it does
    
    function totalAgeYoungerThan0(people, age) {
      let totalAge = 0;
      for ( let i=0; i in people; i++ ) // this will just always return 0 with the arguments we send
        if ( people[i].age < age )
          totalAge += people[i].age;
      return totalAge;
    }
    
    // this code should fail the tests // it does
    
    function totalAgeYoungerThan1(people,tooOld) {
      let totalAge = 0;
      for ( const {age} of people ) // this will throw because our argument isn't iterable
        if ( age < tooOld )
          totalAge += age;
      return totalAge;
    }
    
    // this code should fail the tests // it does
    
    function totalAgeYoungerThan2(xs, age) {
      const [person,...persons] = xs; // this will throw on not iterable
      if ( person===undefined )
        return 0;
      else if ( person.age < age )
        return totalAgeYoungerThan2(persons,age) + person.age;
      else
        return totalAgeYoungerThan2(persons,age);
    }
    
    // this code should theoretically pass the tests // it does
    // it includes the word `for` but not in a for loop
    
    function totalAgeYoungerThan3(people, age) {
      return people
        .filter(person => person.age < age)
        .reduce((formula, person) => formula + person.age, 0)
    }
    
    // passes
    
    function totalAgeYoungerThan4(people, age) {
      return people
        .filter(person => person.age < age)
        .reduce((sum, person) => sum + person.age, 0)
    }
  • 1
    // this code should fail the tests
    
    2
    // const totalAgeYoungerThan = (people, age) => {
    
    3
    //   let totalAge = 0;
    
    4
    //   for (let i = 0; i < people.length; i++) {
    
    5
    //     if (people[i].age < age) totalAge += people[i].age;
    
    6
    //   }
    
    7
    //   return totalAge;
    
    8
    // }
    
    9
    10
    // this code should fail the tests
    
    11
    // const totalAgeYoungerThan = (people, age) => {
    
    12
    //   let totalAge = 0;
    
    13
    //   for (let person of people) {
    
    14
    //     if (person < age) totalAge += person.age;
    
    15
    //   }
    
    16
    //   return totalAge;
    
    17
    // }
    
    18
    19
    // this code should fail the tests
    
    20
    // const totalAgeYoungerThan = (people, age) => {
    
    21
    //   if (people.length === 0) return 0;
    
    22
    //   const [first, ...rest] = people;
    
    23
    //   return (first.age < age ? first.age : 0) + totalAgeYoungerThan(rest, age);
    
    24
    // }
    
    1+
    // this code should fail the tests // it does
    
    2525
    26
    // this code should theoretically pass the tests
    
    3+
    function totalAgeYoungerThan0(people, age) {
    
    4+
      let totalAge = 0;
    
    5+
      for ( let i=0; i in people; i++ ) // this will just always return 0 with the arguments we send
    
    6+
        if ( people[i].age < age )
    
    7+
          totalAge += people[i].age;
    
    8+
      return totalAge;
    
    9+
    }
    
    10+
    11+
    // this code should fail the tests // it does
    
    12+
    13+
    function totalAgeYoungerThan1(people,tooOld) {
    
    14+
      let totalAge = 0;
    
    15+
      for ( const {age} of people ) // this will throw because our argument isn't iterable
    
    16+
        if ( age < tooOld )
    
    17+
          totalAge += age;
    
    18+
      return totalAge;
    
    19+
    }
    
    20+
    21+
    // this code should fail the tests // it does
    
    22+
    23+
    function totalAgeYoungerThan2(xs, age) {
    
    24+
      const [person,...persons] = xs; // this will throw on not iterable
    
    25+
      if ( person===undefined )
    
    26+
        return 0;
    
    27+
      else if ( person.age < age )
    
    28+
        return totalAgeYoungerThan2(persons,age) + person.age;
    
    29+
      else
    
    30+
        return totalAgeYoungerThan2(persons,age);
    
    31+
    }
    
    32+
    33+
    // this code should theoretically pass the tests // it does
    
    2727
    // it includes the word `for` but not in a for loop
    
    28
    // const totalAgeYoungerThan = (people, age) => {
    
    29
    //   return people
    
    30
    //     .filter(person => person.age < age)
    
    31
    //     .reduce((formula, person) => formula + person.age, 0)
    
    32
    // }
    
    3333
    34
    const totalAgeYoungerThan = (people, age) => {
    
    36+
    function totalAgeYoungerThan3(people, age) {
    
    3535
      return people
    
    3636
        .filter(person => person.age < age)
    
    37
        .reduce((sum, person) => sum + person.age, 0)
    
    39+
        .reduce((formula, person) => formula + person.age, 0)
    
    3838
    }
    
    3939
    40
    const hairColorsYoungerThan = (people, age) => {
    
    42+
    // passes
    
    43+
    44+
    function totalAgeYoungerThan4(people, age) {
    
    4141
      return people
    
    4242
        .filter(person => person.age < age)
    
    43
        .map(person => person.hairColor)
    
    47+
        .reduce((sum, person) => sum + person.age, 0)
    
    4444
    }
    

One thing at a time.

First, try to build an object ( to pass to the user solution ) that has its own reduce ( and map and filter defined in terms of reduce ), but has no iterator, is not iterable and cannot be indexed into.

That approach sits better with me than checking native map and friends are actually used - that still tests implementation and not behaviour.

Turns out Proxy was not the right idea - just make it an Object.

I would love to see a generator which accepts [(Gen s, Int)] and uses a set of generators, generates n inputs from each of them, shuffles them, and uses the inputs to feed test cases.

For example, for "Is a number prime?" kata, I'd like to have a composite generator built like compositeGen([(genPrimes, 100), (genOddComposites, 100), (genNegativePrime, 10), (genSquareOfAPrime, 20)]) or something like this, and it would run test cases over 230 shuffled inputs.

Bonus points if such generator were able to generate not only Int, but also (Int, Bool) (input + expected answer) or even (Int, Bool, String) (input, expected answer, and assertion message).


The one thing I can't fix is that if it fails, it's always after 1 test. Because that's what it sees.

module Example (isPrime) where

isPrime :: Int -> Bool
isPrime = odd

I checked it with the actual numbers, and it's correct. It's also slightly faster with those.

Code
Diff
  • const s = `6,3,15,13,1,0`;
    const N = 30e6;
    
    console.time(N);
    
    const input = s.split(',').map(Number);
    const a = Array(N);
    const indexes = Array(N);
    const lastIndexes = Array(N);
    input.forEach((x, i) => ((a[i] = x), (indexes[x] = i), (lastIndexes[x] = i === input.length - 1 ? -1 : i)));
    for (let i = input.length; i < N; i++) {
      const x = a[i - 1];
      if (indexes[x] === i - 1) {
        a[i] = 0;
        if (indexes[0] === undefined) indexes[0] = i;
        lastIndexes[x] = i - 1;
        continue;
      }
      a[i] = i - 1 - lastIndexes[x];
      lastIndexes[x] = i - 1;
      if (indexes[a[i]] === undefined) indexes[a[i]] = i;
    }
    
    console.log(a[N - 1]);
    
    console.timeEnd(N);
  • 11
    const s = `6,3,15,13,1,0`;
    
    2+
    const N = 30e6;
    
    3+
    4+
    console.time(N);
    
    22
    33
    const input = s.split(',').map(Number);
    
    4
    const a = Array(2020);
    
    5
    const indexes = {};
    
    6
    const lastIndexes = {};
    
    7+
    const a = Array(N);
    
    8+
    const indexes = Array(N);
    
    9+
    const lastIndexes = Array(N);
    
    77
    input.forEach((x, i) => ((a[i] = x), (indexes[x] = i), (lastIndexes[x] = i === input.length - 1 ? -1 : i)));
    
    8
    9
    for (let i = input.length; i < 2020; i++) {
    
    11+
    for (let i = input.length; i < N; i++) {
    
    1010
      const x = a[i - 1];
    
    1111
      if (indexes[x] === i - 1) {
    
    1212
        a[i] = 0;
    
    1313
        if (indexes[0] === undefined) indexes[0] = i;
    
    1414
        lastIndexes[x] = i - 1;
    
    1515
        continue;
    
    1616
      }
    
    1717
      a[i] = i - 1 - lastIndexes[x];
    
    1818
      lastIndexes[x] = i - 1;
    
    1919
      if (indexes[a[i]] === undefined) indexes[a[i]] = i;
    
    2020
    }
    
    2121
    22
    console.log(a[2020 - 1]);
    
    24+
    console.log(a[N - 1]);
    
    25+
    26+
    console.timeEnd(N);
    
Code
Diff
  • {-# Options_GHC -O1 #-}
    
    module LongestPath (longestPath) where
    
    import Data.Vector as Vector (fromList,imap,(!),(!?))
    import Data.List (maximumBy,elemIndex)
    import Data.Maybe (fromMaybe)
    import Data.Monoid ((<>))
    import Data.Function (on)
    import Data.Ord (Down(Down))
    
    
    longestPath :: String -> String
    longestPath s = maximumBy order $ ("" :) $ zipWith ( \ i c -> if c == '\n' then "" else getLongestPath i ) [0..] s where
      width = fromMaybe (length s) $ elemIndex '\n' s
      order = (compare `on` length) <> (compare `on` Down)
      v = fromList s
      getLongestPath = memo $ \ i ->
        v ! i : (maximumBy order $ ("" :)
                                 $ map getLongestPath
                                 $ filter ( \ j -> Just (v ! i) < v !? j )
                                 $ [ i-width-2, i-width-1, i-width
                                   , i-1                 , i+1
                                   , i+width  , i+width+1, i+width+2
                                   ]
                )
      memo fn = (Vector.imap (const . fn) (fromList s) !)
  • 1
    {-# OPTIONS_GHC -O1 #-}
    
    1+
    {-# Options_GHC -O1 #-}
    
    22
    33
    module LongestPath (longestPath) where
    
    44
    5
    import Data.Vector.Unboxed as Vector (fromList,(!),(!?))
    
    5+
    import Data.Vector as Vector (fromList,imap,(!),(!?))
    
    66
    import Data.List (maximumBy,elemIndex)
    
    77
    import Data.Maybe (fromMaybe)
    
    88
    import Data.Monoid ((<>))
    
    99
    import Data.Function (on)
    
    1010
    import Data.Ord (Down(Down))
    
    1111
    12
    memo :: (Enum a) => (a -> b) -> (a -> b)
    
    13
    memo fn = (map fn [ toEnum 0 .. ] !!) . fromEnum
    
    1414
    1515
    longestPath :: String -> String
    
    1616
    longestPath s = maximumBy order $ ("" :) $ zipWith ( \ i c -> if c == '\n' then "" else getLongestPath i ) [0..] s where
    
    1717
      width = fromMaybe (length s) $ elemIndex '\n' s
    
    1818
      order = (compare `on` length) <> (compare `on` Down)
    
    1919
      v = fromList s
    
    2020
      getLongestPath = memo $ \ i ->
    
    2121
        v ! i : (maximumBy order $ ("" :)
    
    2222
                                 $ map getLongestPath
    
    2323
                                 $ filter ( \ j -> Just (v ! i) < v !? j )
    
    2424
                                 $ [ i-width-2, i-width-1, i-width
    
    2525
                                   , i-1                 , i+1
    
    2626
                                   , i+width  , i+width+1, i+width+2
    
    2727
                                   ]
    
    2828
                )
    
    27+
      memo fn = (Vector.imap (const . fn) (fromList s) !)
    

My original solution.

Takes even longer than before; still times out with more than 10 big random tests.

Code
Diff
  • {-# OPTIONS_GHC -O1 #-}
    
    module LongestPath (longestPath) where
    
    import Data.Vector.Unboxed as Vector (fromList,(!),(!?))
    import Data.List (maximumBy,elemIndex)
    import Data.Maybe (fromMaybe)
    import Data.Monoid ((<>))
    import Data.Function (on)
    import Data.Ord (Down(Down))
    
    memo :: (Enum a) => (a -> b) -> (a -> b)
    memo fn = (map fn [ toEnum 0 .. ] !!) . fromEnum
    
    longestPath :: String -> String
    longestPath s = maximumBy order $ ("" :) $ zipWith ( \ i c -> if c == '\n' then "" else getLongestPath i ) [0..] s where
      width = fromMaybe (length s) $ elemIndex '\n' s
      order = (compare `on` length) <> (compare `on` Down)
      v = fromList s
      getLongestPath = memo $ \ i ->
        v ! i : (maximumBy order $ ("" :)
                                 $ map getLongestPath
                                 $ filter ( \ j -> Just (v ! i) < v !? j )
                                 $ [ i-width-2, i-width-1, i-width
                                   , i-1                 , i+1
                                   , i+width  , i+width+1, i+width+2
                                   ]
                )
  • 11
    {-# OPTIONS_GHC -O1 #-}
    
    2+
    22
    module LongestPath (longestPath) where
    
    3
    {-
    
    4+
    44
    import Data.Vector.Unboxed as Vector (fromList,(!),(!?))
    
    55
    import Data.List (maximumBy,elemIndex)
    
    66
    import Data.Maybe (fromMaybe)
    
    77
    import Data.Monoid ((<>))
    
    88
    import Data.Function (on)
    
    99
    import Data.Ord (Down(Down))
    
    1010
    2323
                                 $ [ i-width-2, i-width-1, i-width
    
    2424
                                   , i-1                 , i+1
    
    2525
                                   , i+width  , i+width+1, i+width+2
    
    2626
                                   ]
    
    2727
                )
    
    28
    29
     -}
    
    30
    31
    import           Control.Monad
    
    32
    import           Data.Function   ((&))
    
    33
    import           Data.Ix         (inRange)
    
    34
    import           Data.List.Split (chunksOf)
    
    35
    import           Data.Maybe
    
    36
    import           Data.Vector     (Vector)
    
    37
    import qualified Data.Vector     as V
    
    38
    39
    40
    minimumOn :: (Ord b) => (a -> b) -> [a] -> a
    
    41
    minimumOn f [] = error "Data.List.Extra.minimumOn: empty list"
    
    42
    minimumOn f (x:xs) = g x (f x) xs
    
    43
        where
    
    44
            g v mv [] = v
    
    45
            g v mv (x:xs) | mx < mv = g x mx xs
    
    46
                          | otherwise = g v mv xs
    
    47
                where mx = f x
    
    48
    49
    longestPath :: String -> String
    
    50
    longestPath = solve . lines
    
    51
    52
    maximumCell :: [(Int, [Char])] -> (Int, [Char])
    
    53
    maximumCell = minimumOn (\(n, s) -> (-n, s))
    
    54
    55
    solve :: [[Char]] -> [Char]
    
    56
    solve input = knot & V.toList & V.concat & V.toList & maximumCell & snd
    
    57
     where
    
    58
      grid   = V.fromList $ V.fromList <$> input
    
    59
      height = length input
    
    60
      width  = length (head input)
    
    61
      knot   = V.fromList $ do
    
    62
        row <- [0 .. height - 1]
    
    63
        pure $ V.fromList $ seqLenAt grid knot row <$> [0 .. width - 1]
    
    64
    65
    around :: [(Int, Int)]
    
    66
    around = (,) <$> [-1 .. 1] <*> [-1 .. 1] & filter (/= (0, 0))
    
    67
    68
    seqLenAt
    
    69
      :: Vector (Vector Char)
    
    70
      -> Vector (Vector (Int, [Char]))
    
    71
      -> Int
    
    72
      -> Int
    
    73
      -> (Int, [Char])
    
    74
    seqLenAt grid knot row col = maximumCell candidates
    
    75
     where
    
    76
      hereCh     = (grid V.! row) V.! col
    
    77
      candidates = (1, [hereCh]) : do
    
    78
        (dx, dy) <- around
    
    79
        let (ty, tx) = (row + dy, col + dx)
    
    80
        thereCh <- maybeToList $ (V.!? ty) >=> (V.!? tx) $ grid
    
    81
        guard $ hereCh < thereCh
    
    82
        let (n, s) = (knot V.! ty) V.! tx
    
    83
        pure (n + 1, hereCh : s)
    
Code
Diff
  • module ClosestPoint (closestPoint) where
    
    import Preloaded (Point,dist)
    import Data.List (minimumBy)
    import Data.Function (on)
    
    -- extracts from a list of points the closest to a given point
    closestPoint :: Point -> [Point] -> Point
    -- closestPoint point ps = snd $ minimum $ map ( \ p -> ( dist point p, p ) ) ps
    -- closestPoint point = snd . minimum . map ((,) =<< dist point)
    
    closestPoint point = minimumBy (compare `on` dist point)
    
    -- because calling `dist` twice per comparison is inconsequential compared to _your_ ease of reading and writing
    -- if you want more performance, get a bigger conmputer. you're not Google. your time is more expensive
  • 11
    module ClosestPoint (closestPoint) where
    
    22
    33
    import Preloaded (Point,dist)
    
    4+
    import Data.List (minimumBy)
    
    5+
    import Data.Function (on)
    
    44
    55
    -- extracts from a list of points the closest to a given point
    
    66
    closestPoint :: Point -> [Point] -> Point
    
    7
    closestPoint point [] = undefined
    
    8
    closestPoint point (p:ps) = closest (dist point p) p ps where
    
    9+
    -- closestPoint point ps = snd $ minimum $ map ( \ p -> ( dist point p, p ) ) ps
    
    10+
    -- closestPoint point = snd . minimum . map ((,) =<< dist point)
    
    99
    10
      closest :: Float -> Point -> [Point] -> Point
    
    11
      closest dist1 p1 []      = p1
    
    12
      closest dist1 p1 (p2:ps) | dist1 < dist2 = closest dist1 p1 ps
    
    13
                               | otherwise     = closest dist2 p2 ps
    
    14
                               where dist2 = dist point p2
    
    12+
    closestPoint point = minimumBy (compare `on` dist point)
    
    13+
    14+
    -- because calling `dist` twice per comparison is inconsequential compared to _your_ ease of reading and writing
    
    15+
    -- if you want more performance, get a bigger conmputer. you're not Google. your time is more expensive
    

Note that dist is called twice per comparison, and there are length ps - 1 comparisons

Code
Diff
  • {-# Language ViewPatterns #-}
    
    module ClosestPoint (closestPoint) where
    
    import Debug.Trace
    
    import Preloaded (Point,dist)
    import Data.List (minimumBy)
    
    -- extracts from a list of points the closest to a given point
    closestPoint :: Point -> [Point] -> Point
    closestPoint _ [] = undefined
    closestPoint point (traceShowId . trace "<hr>" -> ps) = minimumBy (\x y -> compare (dist point x) (dist point y)) ps
  • 1+
    {-# Language ViewPatterns #-}
    
    2+
    11
    module ClosestPoint (closestPoint) where
    
    22
    5+
    import Debug.Trace
    
    6+
    33
    import Preloaded (Point,dist)
    
    44
    import Data.List (minimumBy)
    
    55
    66
    -- extracts from a list of points the closest to a given point
    
    77
    closestPoint :: Point -> [Point] -> Point
    
    88
    closestPoint _ [] = undefined
    
    9
    closestPoint point ps = minimumBy (\x y -> compare (dist point x) (dist point y)) ps
    
    13+
    closestPoint point (traceShowId . trace "<hr>" -> ps) = minimumBy (\x y -> compare (dist point x) (dist point y)) ps
    

Notice that dist isn't called at all for empty and singleton lists, because laziness

Code
Diff
  • {-# Language ViewPatterns #-} -- it's called a "pragma", in this case a language extension
    
    module ClosestPoint (closestPoint) where
    
    import Debug.Trace
    
    import Preloaded (Point,dist)
    import Data.List (minimum)
    
    -- extracts from a list of points the closest to a given point
    closestPoint :: Point -> [Point] -> Point
    closestPoint point (traceShowId . trace "<hr>" -> ps) = snd $ minimum $ map ( \ p -> ( dist point p , p ) ) ps
  • 1+
    {-# Language ViewPatterns #-} -- it's called a "pragma", in this case a language extension
    
    2+
    11
    module ClosestPoint (closestPoint) where
    
    22
    5+
    import Debug.Trace
    
    6+
    33
    import Preloaded (Point,dist)
    
    44
    import Data.List (minimum)
    
    55
    66
    -- extracts from a list of points the closest to a given point
    
    77
    closestPoint :: Point -> [Point] -> Point
    
    8
    closestPoint point ps = snd $ minimum $ map ( \ p -> ( dist point p , p ) ) ps
    
    12+
    closestPoint point (traceShowId . trace "<hr>" -> ps) = snd $ minimum $ map ( \ p -> ( dist point p , p ) ) ps
    

We may as well get rid of closestPoint _ [] -> undefined. If we get [], we throw; doesn't really matter how we do that. I've added a test for it as well. It's now actually minimum that throws.

Tuples have a defined order in Haskell, just as numbers ( and everything else - except functions. and complex numbers ) do. If the first values of the respective tuples compare as smaller, the tuples compare as smaller. And bigger as bigger. If they're equal, the second values get compared, then third, fourth, .. When we run out of values, we might find they're actually equal, but only if all values are equal.

To prevent multiple calculations of dist, we decorate the input list with distances ( putting the distance first and the point second ), take the minimum over the list of tuples, then drop the distance again. Works the same as that accumulator for the fold!

Code
Diff
  • module ClosestPoint (closestPoint) where
    
    import Preloaded (Point,dist)
    import Data.List (minimum)
    
    -- extracts from a list of points the closest to a given point
    closestPoint :: Point -> [Point] -> Point
    closestPoint point ps = snd $ minimum $ map ( \ p -> ( dist point p , p ) ) ps
  • 11
    module ClosestPoint (closestPoint) where
    
    22
    33
    import Preloaded (Point,dist)
    
    4
    import Data.List (minimumBy)
    
    4+
    import Data.List (minimum)
    
    55
    66
    -- extracts from a list of points the closest to a given point
    
    77
    closestPoint :: Point -> [Point] -> Point
    
    8
    closestPoint _ [] = undefined
    
    9
    closestPoint point ps = minimumBy (\x y -> compare (dist point x) (dist point y)) ps
    
    8+
    closestPoint point ps = snd $ minimum $ map ( \ p -> ( dist point p , p ) ) ps
    

closestPoint _ [] does not make sense. result can't be any point that is not in the list. ( tests have been updated to check for this. )

point is in scope in closest.
it's not necessary to pass it as an argument.
that's why you use a where-clause; if it had been a top-level definition, it would have needed its own point.

in closest: dist1 = dist point p1, dist2 = dist point p2.

Code
Diff
  • module ClosestPoint (closestPoint) where
    
    import Preloaded (Point,dist)
    
    -- extracts from a list of points the closest to a given point
    closestPoint :: Point -> [Point] -> Point
    closestPoint point [] = undefined
    closestPoint point (p:ps) = closest (dist point p) p ps where
    
      closest :: Float -> Point -> [Point] -> Point
      closest dist1 p1 []      = p1
      closest dist1 p1 (p2:ps) | dist1 < dist2 = closest dist1 p1 ps
                               | otherwise     = closest dist2 p2 ps
                               where dist2 = dist point p2
  • 11
    module ClosestPoint (closestPoint) where
    
    22
    33
    import Preloaded (Point,dist)
    
    44
    55
    -- extracts from a list of points the closest to a given point
    
    66
    closestPoint :: Point -> [Point] -> Point
    
    7
    closestPoint point [] = point
    
    8
    closestPoint point (p : ps) = closest point (dist point p) p ps
    
    9
      where
    
    10
          closest :: Point ->  Float -> Point -> [Point] -> Point
    
    11
          closest p0 initialDist p1 [] = p1
    
    12
          closest p0 initialDist p1 (p2 : ps)
    
    13
            | initialDist < dis = closest p0 initialDist p1 ps
    
    14
            | otherwise         = closest p0 dis p2 ps
    
    15
            where 
    
    16
                dis = dist p0 p2
    
    7+
    closestPoint point [] = undefined
    
    8+
    closestPoint point (p:ps) = closest (dist point p) p ps where
    
    9+
    10+
      closest :: Float -> Point -> [Point] -> Point
    
    11+
      closest dist1 p1 []      = p1
    
    12+
      closest dist1 p1 (p2:ps) | dist1 < dist2 = closest dist1 p1 ps
    
    13+
                               | otherwise     = closest dist2 p2 ps
    
    14+
                               where dist2 = dist point p2
    
Loading more items...