OCaml Module error - debugging

When I trying to pass functor Board to Sudoku it has following error
What should i do in the .ml file to match the interface? How do i initialize the module Board and pass it into the Sudoku Module?
error: The implementation modularSudoku.ml
does not match the interface modularSudoku.cmi:
Modules do not match:
functor (B : Board1) ->
sig
val pick : B.grid -> (B.grid * B.location * B.grid) option
val update_at_loc :
B.grid -> B.location -> square -> B.grid option
val update_grid : B.grid -> B.location -> B.grid option
val is_solved : B.grid -> bool
val solve_sudoku : B.grid -> B.grid list -> B.grid list
val backtrack : B.grid list -> B.grid list
end
is not included in
functor
(Board : sig
type location
type grid
val first_location : location
val next : location -> location option
val same_row_col_block : location -> location -> bool
val get_square : grid -> location -> square
val change_square : grid -> location -> square -> grid
end) ->
sig
val pick :
Board.grid ->
(Board.grid * Board.location * Board.grid) option
val update_grid :
Board.grid -> Board.location -> Board.grid option
val is_solved : Board.grid -> bool
val solve_sudoku :
Board.grid -> Board.grid list -> Board.grid list
end
Modules do not match:
sig
type location
type grid
val first_location : location
val next : location -> location option
val same_row_col_block : location -> location -> bool
val get_square : grid -> location -> square
val change_square : grid -> location -> square -> grid
end
is not included in
Board1
Type declarations do not match:
type location
is not included in
type location = int * int
Here is Sudoku.mli file
type square = Picked of int | Possible of int list;;
module Sudoku :
functor (Board: sig
type location;;
type grid;;
val first_location: location;;
val next : location -> location option;;
val same_row_col_block : location -> location -> bool;;
val get_square : grid -> location -> square;;
val change_square : grid -> location -> square -> grid;;
end) -> (sig
open Board;;
val pick : grid -> (grid * location * grid) option;;
val update_grid : grid -> location -> grid option;;
val is_solved : grid -> bool;;
val solve_sudoku : grid -> grid list -> grid list;;
end)
;;
module Board1 : sig
type location = (int*int);;
type grid = square list;;
val first_location: location;;
val next : location -> location option;;
val same_row_col_block : location -> location -> bool;;
val get_square : grid -> location -> square;;
val change_square : grid -> location -> square -> grid;;
end
Here is Sudoku.ml file
type square = Picked of int | Possible of int list;;
let create_grid l = List.map (fun x -> match x with 0 -> Possible [1;2;3;4;5;6;7;8;9] | _ -> Picked x) l;;
(*test*)
let easyGrid = create_grid
[ 0 ; 6 ; 0 ; 0 ; 4 ; 0 ; 0 ; 0 ; 0
; 3 ; 0 ; 7 ; 1 ; 0 ; 9 ; 0 ; 0 ; 6
; 0 ; 9 ; 4 ; 2 ; 0 ; 5 ; 7 ; 0 ; 0
; 0 ; 3 ; 1 ; 0 ; 0 ; 0 ; 4 ; 0 ; 0
; 2 ; 0 ; 0 ; 4 ; 0 ; 7 ; 0 ; 0 ; 5
; 0 ; 0 ; 6 ; 0 ; 0 ; 0 ; 9 ; 7 ; 0
; 0 ; 0 ; 2 ; 3 ; 0 ; 8 ; 6 ; 1 ; 0
; 6 ; 0 ; 0 ; 9 ; 0 ; 2 ; 8 ; 0 ; 4
; 0 ; 0 ; 0 ; 0 ; 1 ; 0 ; 0 ; 9 ; 0];;
module type Board1 = sig
type location = (int*int);;
type grid = square list;;
val first_location: location;;
val next : location -> location option;;
val same_row_col_block : location -> location -> bool;;
val get_square : grid -> location -> square;;
val change_square : grid -> location -> square -> grid;;
end
module Board1 =
struct
type location = (int*int)
type grid = square list
let first_location = (0,0)
let next (i,j) =
if j+1 = 9 then
if i+1 = 9 then None
else Some (i+1,0)
else Some (i,j+1)
let same_row_col_block (i1,j1) (i2,j2) =
(i1 = i2) || (j1 = j2) || ((i1/3 = i2/3) && (j1/3 = j2/3))
let get_square grid (i, j) = List.nth grid (9*i + j)
let change_square grid (i, j) square =
let k = i * 9 + j in
let rec helper t acc n =
if n = k then List.rev_append acc (square :: (List.tl t))
else helper (List.tl t) (List.hd t :: acc) (n+1) in
helper grid [] 0
end;;
module Sudoku =
functor (B:Board1)->
struct
let pick grid =
let rec helper loc =
match B.get_square grid loc with
| Picked _ -> (match B.next loc with
| None -> None
| Some loc1 -> helper loc1)
| Possible l ->
(match l with
| [] -> None
| x :: xs -> let new_grid = B.change_square grid loc (Picked x) in
let choice_grid = B.change_square grid loc (Possible xs) in
Some (new_grid, loc, choice_grid)) in
helper B.first_location
let update_at_loc grid loc square =
let square1 = B.get_square grid loc in
match square,square1 with
| Picked x, Picked y -> if x = y then None else Some grid
| Picked x, Possible l ->
if List.mem x l
then let l' = List.filter (fun y -> not (y = x)) l in
if List.length l' == 0 then None else Some (B.change_square grid loc (Possible l'))
else Some grid
| _ -> assert false
let update_grid grid loc =
let square = B.get_square grid loc in
let apply_constraint loc1 grid =update_at_loc grid loc1 square in
let rec help loc1 grid =
let g =
if loc = loc1
then Some grid
else if B.same_row_col_block loc loc1
then apply_constraint loc1 grid
else Some grid in
match g with
| None -> None
| Some newgrid ->
(match B.next loc1 with
| None -> g
| Some loc2 -> help loc2 newgrid) in
help B.first_location grid
let is_solved grid =
let rec helper loc =
match B.get_square grid loc with
| Possible _ -> false
| Picked _ -> match B.next loc with
| None -> true
| Some loc1 -> helper loc1 in
helper B.first_location
let rec solve_sudoku grid choices =
match pick grid with
| None -> if is_solved grid then (*let () = print_sudoku grid in*) grid :: backtrack choices
else backtrack choices
| Some (g,loc,ch) ->
(match update_grid g loc with
| None -> backtrack (ch::choices)
| Some grid -> (*let () = print_sudoku grid in*)
solve_sudoku grid (ch::choices))
and backtrack choices =
match choices with
| [] -> []
| g :: grids -> solve_sudoku g grids
end
;;

In your .mli, the functor Sudoku is specified to expect an argument module with types location and grid, but with no restriction on what these types are. For example, according to the spec in the .mli, I should be able to apply
module A = struct type location = bool type grid = unit (* ... *) end
module S = Sudoku(A)
However, in the .ml you define the functor such that it requires the argument to have these types equal to int * int and square list, respectively. Clearly, that is more restrictive (fewer modules match that argument signature, in particular, not the above A), and hence the functor itself does not match the more liberal specification in the .mli (this is also known as contravariance).
Is there a reason why you don't just annotate the functor parameter B with the same signature Board1 in the .mli as well? Then it would clearly work.

Related

Ocaml- partial derivative of a regular expression

I got this code:
type regexp =
| V (* void *)
| E (* epsilon *)
| C of char (* char *)
| U of regexp * regexp (* a + b *)
| P of regexp * regexp (* a.b *)
| S of regexp (* a* *)
;;
...
module ReS = Set.Make (struct
type t = regexp
let compare = compare
end)
(* module/type for pairs of sets of regular expressions *)
module RePS = Set.Make (struct
type t = ReS.t * ReS.t
let compare = compare
end)
(*module/type for set of chars *)
module CS = Set.Make(Char)
let ewps = ReS.exists ewp;;
let atmost_epsilons = ReS.for_all atmost_epsilon;;
let infinitys = ReS.exists infinity;;
let rigth_concat s = function
| V -> ReS.empty
| E -> s
| r -> ReS.map (fun e -> P (e,r)) s
;;
let ( *.* ) = rigth_concat;;
(* partial derivative of a regular expression *)
let rec pd a re = function
| V | E -> ReS.empty
| C b when b=a -> ReS.singleton E
| C b -> ReS.empty
| U (r, s) -> ReS.union (pd a r) (pd a s)
| P (r, s) when ewp a -> ReS.union ((pd a r) *.* s) (pd a s)
| P (r, s) -> (pd a r) *.* s
| S r as re -> (pd a r) *.* re
;;
let rec unions f s =
ReS.fold (fun re acc -> ReS.union (f re) acc ) s ReS.empty
;;
let rec pds a s = unions (pd a) s;;
let rec pdw (sr: ReS.t) = function
| [] -> sr
| a::tl -> pds a (pdw sr tl)
;;
I checked the types of return values and i think they are correct, but it returns the following error and I am not sure why.
This expression has type regexp -> ReS.t but an expression was
expected of type ReS.t
In function "pd" in line that has error
| U (r, s) -> ReS.union (pd a r) (pd a s)
I believe your problem is caused by the fact that function supplies an implicit parameter. This expression:
function None -> 0 | Some x -> x
is a function with one parameter. So in your case you have defined pd to have three parameters. It looks to me like you're expecting it to have two parameters.
You can probably change your function ... to match re with instead. Or you can remove the explicit re parameter, and use the parameter that's implicit in function.

Knuth-Morris-Pratt implementation in Haskell -- Index out of bounds

I've used the pseudocode from Wikipedia in an attempt to write a KMP algorithm in Haskell.
It's giving "index out of bounds" when I try to search beyond the length of the pattern and I can't seem to find the issue; my "fixes" have only ruined the result.
import Control.Monad
import Control.Lens
import qualified Data.ByteString.Char8 as C
import qualified Data.Vector.Unboxed as V
(!) :: C.ByteString -> Int -> Char
(!) = C.index
-- Make the table for the KMP. Directly from Wikipedia. Works as expected for inputs from Wikipedia article.
mkTable :: C.ByteString -> V.Vector Int
mkTable pat = make 2 0 (ix 0 .~ (negate 1) $ V.replicate l 0)
where
l = C.length pat
make :: Int -> Int -> V.Vector Int -> V.Vector Int
make p c t
| p >= l = t
| otherwise = proc
where
proc | pat ! (p-1) == pat ! c
= make (p+1) (c+1) (ix p .~ (c+1) $ t)
| c > 0 = make p (t V.! c) t
| otherwise = make (p+1) c (ix p .~ 0 $ t)
kmp :: C.ByteString -> C.ByteString -> V.Vector Int -> Int
kmp text pat tbl = search 0 0
where
l = C.length text
search m i
| m + i >= l = l
| otherwise = cond
where
-- The conditions for the loop, given in the wiki article
cond | pat ! i == text ! (m+i)
= if i == C.length pat - 1
then m
else search m (i+1)
| tbl V.! i > (-1)
= search (m + i - (tbl V.! i)) (tbl V.! i)
| otherwise
= search 0 (m+1)
main :: IO()
main = do
t <- readLn
replicateM_ t $ do
text <- C.getLine
pat <- C.getLine
putStrLn $ kmp text pat (mkTable pat)
Simple solution: I mixed up m and i in the last condition of kmp.
| otherwise = search 0 (m+1)
Becomes
| otherwise = search (m+1) 0
And the issue is resolved.
Aside from that, it's necessary to use unboxed arrays in the ST monad or the table generation takes an absurd amount of time.

Why is the "better" digit-listing function slower?

I was playing around with Project Euler #34, and I wrote these functions:
import Data.Time.Clock.POSIX
import Data.Char
digits :: (Integral a) => a -> [Int]
digits x
| x < 10 = [fromIntegral x]
| otherwise = let (q, r) = x `quotRem` 10 in (fromIntegral r) : (digits q)
digitsByShow :: (Integral a, Show a) => a -> [Int]
digitsByShow = map (\x -> ord x - ord '0') . show
I thought that for sure digits has to be the faster one, as we don't convert to a String. I could not have been more wrong. I ran the two versions via pe034:
pe034 digitFunc = sum $ filter sumFactDigit [3..2540160]
where
sumFactDigit :: Int -> Bool
sumFactDigit n = n == (sum $ map sFact $ digitFunc n)
sFact :: Int -> Int
sFact n
| n == 0 = 1
| n == 1 = 1
| n == 2 = 2
| n == 3 = 6
| n == 4 = 24
| n == 5 = 120
| n == 6 = 720
| n == 7 = 5040
| n == 8 = 40320
| n == 9 = 362880
main = do
begin <- getPOSIXTime
print $ pe034 digitsByShow -- or digits
end <- getPOSIXTime
print $ end - begin
After compiling with ghc -O, digits consistently takes .5 seconds, while digitsByShow consistently takes .3 seconds. Why is this so? Why is the function which stays within Integer arithmetic slower, whereas the function which goes into string comparison is faster?
I ask this because I come from programming in Java and similar languages, where the % 10 trick of generating digits is way faster than the "convert to String" method. I haven't been able to wrap my head around the fact that converting to a string could be faster.
This is the best I can come up with.
digitsV2 :: (Integral a) => a -> [Int]
digitsV2 n = go n []
where
go x xs
| x < 10 = fromIntegral x : xs
| otherwise = case quotRem x 10 of
(q,r) -> go q (fromIntegral r : xs)
when compiled with -O2 and tested with Criterion
digits runs in 470.4 ms
digitsByShow runs in 421.8 ms
digitsV2 runs in 258.0 ms
results may vary
edit:
I am not sure why building the list like this helps so much.
But you can improve your codes speed by strictly evaluating quotRem x 10
You can do this with BangPatterns
| otherwise = let !(q, r) = x `quotRem` 10 in (fromIntegral r) : (digits q)
or with case
| otherwise = case quotRem x 10 of
(q,r) -> fromIntegral r : digits q
Doing this drops digits down to 323.5 ms
edit: time without using Criterion
digits = 464.3 ms
digitsStrict = 328.2 ms
digitsByShow = 259.2 ms
digitV2 = 252.5 ms
note: The criterion package measures software performance.
Let's investigate why #No_signal's solution is faster.
I made three runs of ghc:
ghc -O2 -ddump-simpl digits.hs >digits.txt
ghc -O2 -ddump-simpl digitsV2.hs >digitsV2.txt
ghc -O2 -ddump-simpl show.hs >show.txt
digits.hs
digits :: (Integral a) => a -> [Int]
digits x
| x < 10 = [fromIntegral x]
| otherwise = let (q, r) = x `quotRem` 10 in (fromIntegral r) : (digits q)
main = return $ digits 1
digitsV2.hs
digitsV2 :: (Integral a) => a -> [Int]
digitsV2 n = go n []
where
go x xs
| x < 10 = fromIntegral x : xs
| otherwise = let (q, r) = x `quotRem` 10 in go q (fromIntegral r : xs)
main = return $ digits 1
show.hs
import Data.Char
digitsByShow :: (Integral a, Show a) => a -> [Int]
digitsByShow = map (\x -> ord x - ord '0') . show
main = return $ digitsByShow 1
If you'd like to view the complete txt files, I placed them on ideone (rather than paste a 10000 char dump here):
digits.txt
digitsV2.txt
show.txt
If we carefully look through digits.txt, it appears that this is the relevant section:
lvl_r1qU = __integer 10
Rec {
Main.$w$sdigits [InlPrag=[0], Occ=LoopBreaker]
:: Integer -> (# Int, [Int] #)
[GblId, Arity=1, Str=DmdType <S,U>]
Main.$w$sdigits =
\ (w_s1pI :: Integer) ->
case integer-gmp-1.0.0.0:GHC.Integer.Type.ltInteger#
w_s1pI lvl_r1qU
of wild_a17q { __DEFAULT ->
case GHC.Prim.tagToEnum# # Bool wild_a17q of _ [Occ=Dead] {
False ->
let {
ds_s16Q [Dmd=<L,U(U,U)>] :: (Integer, Integer)
[LclId, Str=DmdType]
ds_s16Q =
case integer-gmp-1.0.0.0:GHC.Integer.Type.quotRemInteger
w_s1pI lvl_r1qU
of _ [Occ=Dead] { (# ipv_a17D, ipv1_a17E #) ->
(ipv_a17D, ipv1_a17E)
} } in
(# case ds_s16Q of _ [Occ=Dead] { (q_a11V, r_X12h) ->
case integer-gmp-1.0.0.0:GHC.Integer.Type.integerToInt r_X12h
of wild3_a17c { __DEFAULT ->
GHC.Types.I# wild3_a17c
}
},
case ds_s16Q of _ [Occ=Dead] { (q_X12h, r_X129) ->
case Main.$w$sdigits q_X12h
of _ [Occ=Dead] { (# ww1_s1pO, ww2_s1pP #) ->
GHC.Types.: # Int ww1_s1pO ww2_s1pP
}
} #);
True ->
(# GHC.Num.$fNumInt_$cfromInteger w_s1pI, GHC.Types.[] # Int #)
}
}
end Rec }
digitsV2.txt:
lvl_r1xl = __integer 10
Rec {
Main.$wgo [InlPrag=[0], Occ=LoopBreaker]
:: Integer -> [Int] -> (# Int, [Int] #)
[GblId, Arity=2, Str=DmdType <S,U><L,U>]
Main.$wgo =
\ (w_s1wh :: Integer) (w1_s1wi :: [Int]) ->
case integer-gmp-1.0.0.0:GHC.Integer.Type.ltInteger#
w_s1wh lvl_r1xl
of wild_a1dp { __DEFAULT ->
case GHC.Prim.tagToEnum# # Bool wild_a1dp of _ [Occ=Dead] {
False ->
case integer-gmp-1.0.0.0:GHC.Integer.Type.quotRemInteger
w_s1wh lvl_r1xl
of _ [Occ=Dead] { (# ipv_a1dB, ipv1_a1dC #) ->
Main.$wgo
ipv_a1dB
(GHC.Types.:
# Int
(case integer-gmp-1.0.0.0:GHC.Integer.Type.integerToInt ipv1_a1dC
of wild2_a1ea { __DEFAULT ->
GHC.Types.I# wild2_a1ea
})
w1_s1wi)
};
True -> (# GHC.Num.$fNumInt_$cfromInteger w_s1wh, w1_s1wi #)
}
}
end Rec }
I actually couldn't find the relevant section for show.txt. I'll work on that later.
Right off the bat, digitsV2.hs produces shorter code. That's probably a good sign for it.
digits.hs seems to be following this psuedocode:
def digits(w_s1pI):
if w_s1pI < 10: return [fromInteger(w_s1pI)]
else:
ds_s16Q = quotRem(w_s1pI, 10)
q_X12h = ds_s16Q[0]
r_X12h = ds_s16Q[1]
wild3_a17c = integerToInt(r_X12h)
ww1_s1pO = r_X12h
ww2_s1pP = digits(q_X12h)
ww2_s1pP.pushFront(ww1_s1pO)
return ww2_s1pP
digitsV2.hs seems to be following this psuedocode:
def digitsV2(w_s1wh, w1_s1wi=[]): # actually disguised as go(), as #No_signal wrote
if w_s1wh < 10:
w1_s1wi.pushFront(fromInteger(w_s1wh))
return w1_s1wi
else:
ipv_a1dB, ipv1_a1dC = quotRem(w_s1wh, 10)
w1_s1wi.pushFront(integerToIn(ipv1a1dC))
return digitsV2(ipv1_a1dC, w1_s1wi)
It might not be that these functions mutate lists like my psuedocode suggests, but this immediately suggests something: it looks as if digitsV2 is fully tail-recursive, whereas digits is actually not (may have to use some Haskell trampoline or something). It appears as if Haskell needs to store all the remainders in digits before pushing them all to the front of the list, whereas it can just push them and forget about them in digitsV2. This is purely speculation, but it is well-founded speculation.

Fusion optimization with intermediate values

Will GHC transform an expression with intermediate values as efficiently as one without?
e.g.
main = print $ f ["aa", "bb", "cc"]
f x =
let a = map (map toUpper) x
b = filter (\z -> 'C' /= head z) a
c = foldl1 (++) b
in c
seems to have very different core output (with -ddump-simple) than with
f x = foldl1 (++) $ filter (\z -> 'C' /= head z) $ map (map toUpper) x
Could an expression with intermediate values take (significantly) longer to evaluate?
Linear use of intermediate let bindings is equivalent to putting (.) between the values.
GHC will fuse through such pipelines. You can see from the results of -ddump-simpl-stats
With let Bindings:
15 RuleFired
1 ++
1 Class op /=
1 Class op show
1 Class op showList
1 filter
1 fold/build
1 foldr/app
1 map
1 neChar#->case
3 unpack
3 unpack-list
Using a pipeline:
15 RuleFired
1 ++
1 Class op /=
1 Class op show
1 Class op showList
1 filter
1 fold/build
1 foldr/app
1 map
1 neChar#->case
3 unpack
3 unpack-list
And the same fused worker:
With let Bindings:
Main.main_go =
\ (ds_aAz :: [[GHC.Types.Char]]) ->
case ds_aAz of _ {
[] -> GHC.Types.[] # [GHC.Types.Char];
: y_aAE ys_aAF ->
case GHC.Base.map
# GHC.Types.Char # GHC.Types.Char GHC.Unicode.toUpper y_aAE
of wild1_azI {
[] ->
GHC.List.badHead
`cast` (UnsafeCo (forall a_azK. a_azK) [[GHC.Types.Char]]
:: (forall a_azK. a_azK) ~ [[GHC.Types.Char]]);
: x_azM ds1_azN ->
case x_azM of _ { GHC.Types.C# c2_aAa ->
case c2_aAa of _ {
__DEFAULT ->
GHC.Types.: # [GHC.Types.Char] wild1_azI (Main.main_go ys_aAF);
'C' -> Main.main_go ys_aAF
}
Pipeline:
Main.main_go =
\ (ds_aAA :: [[GHC.Types.Char]]) ->
case ds_aAA of _ {
[] -> GHC.Types.[] # [GHC.Types.Char];
: y_aAF ys_aAG ->
case GHC.Base.map
# GHC.Types.Char # GHC.Types.Char GHC.Unicode.toUpper y_aAF
of wild1_azB {
[] ->
GHC.List.badHead
`cast` (UnsafeCo (forall a_azD. a_azD) [[GHC.Types.Char]]
:: (forall a_azD. a_azD) ~ [[GHC.Types.Char]]);
: x_azF ds1_azG ->
case x_azF of _ { GHC.Types.C# c2_aA3 ->
case c2_aA3 of _ {
__DEFAULT ->
GHC.Types.: # [GHC.Types.Char] wild1_azB (Main.main_go ys_aAG);
'C' -> Main.main_go ys_aAG
}
}
Did you forget to compile with -O2 ?

Filter an array or list by consecutive pairs based on a matching rule

This is probably trivial, and I do have a solution but I'm not happy with it. Somehow, (much) simpler forms don't seem to work and it gets messy around the corner cases (either first, or last matching pairs in a row).
To keep it simple, let's define the matching rule as any two or more numbers that have a difference of two. Example:
> filterTwins [1; 2; 4; 6; 8; 10; 15; 17]
val it : int list = [2; 4; 6; 8; 10; 15; 17]
The code I currently use is this, which just feels sloppy and overweight:
let filterTwins list =
let func item acc =
let prevItem, resultList = acc
match prevItem, resultList with
| 0, []
-> item, []
| var, [] when var - 2 = item
-> item, item::var::resultList
| var, hd::tl when var - 2 = item && hd <> var
-> item, item::var::resultList
| var, _ when var - 2 = item
-> item, item::resultList
| _
-> item, resultList
List.foldBack func list (0, [])
|> snd
I intended my own original exercise to experiment with List.foldBack, large lists and parallel programming (which went well) but ended up messing with the "easy" part...
Guide through the answers
Daniel's last, 113 characters*, easy to follow, slow
Kvb's 2nd, 106 characters* (if I include the function), easy, but return value requires work
Stephen's 2nd, 397 characters*, long winded and comparably complex, but fastest
Abel's, 155 characters*, based on Daniel's, allows duplicates (this wasn't a necessity, btw) and is relatively fast.
There were more answers, but the above were the most distinct, I believe. Hope I didn't hurt anybody's feelings by accepting Daniel's answer as solution: each and every one solution deserves to be the selected answer(!).
* counting done with function names as one character
Would this do what you want?
let filterTwins l =
let rec filter l acc flag =
match l with
| [] -> List.rev acc
| a :: b :: rest when b - 2 = a ->
filter (b::rest) (if flag then b::acc else b::a::acc) true
| _ :: t -> filter t acc false
filter l [] false
This is terribly inefficient, but here's another approach using more built-in functions:
let filterTwinsSimple l =
l
|> Seq.pairwise
|> Seq.filter (fun (a, b) -> b - 2 = a)
|> Seq.collect (fun (a, b) -> [a; b])
|> Seq.distinct
|> Seq.toList
Maybe slightly better:
let filterTwinsSimple l =
seq {
for (a, b) in Seq.pairwise l do
if b - 2 = a then
yield a
yield b
}
|> Seq.distinct
|> Seq.toList
How about this?
let filterPairs f =
let rec filter keepHead = function
| x::(y::_ as xs) when f x y -> x::(filter true xs)
| x::xs ->
let rest = filter false xs
if keepHead then x::rest else rest
| _ -> []
filter false
let test = filterPairs (fun x y -> y - x = 2) [1; 2; 4; 6; 8; 10; 15; 17]
Or if all of your list's items are unique, you could do this:
let rec filterPairs f s =
s
|> Seq.windowed 2
|> Seq.filter (fun [|a;b|] -> f a b)
|> Seq.concat
|> Seq.distinct
let test = filterPairs (fun x y -> y - x = 2) [1; 2; 4; 6; 8; 10; 15; 17]
EDIT
Or here's another alternative which I find elegant. First define a function for breaking a list into a list of groups of consecutive items satisfying a predicate:
let rec groupConsec f = function
| [] -> []
| x::(y::_ as xs) when f x y ->
let (gp::gps) = groupConsec f xs
(x::gp)::gps
| x::xs -> [x]::(groupConsec f xs)
Then, build your function by collecting all results back together, discarding any singletons:
let filterPairs f =
groupConsec f
>> List.collect (function | [_] -> [] | l -> l)
let test = filterPairs (fun x y -> y - x = 2) [1; 2; 4; 6; 8; 10; 15; 17]
The following solution is in the spirit of your own, but I use a discriminate union to encapsulate aspects of the algorithm and reign in the madness a bit:
type status =
| Keep of int
| Skip of int
| Tail
let filterTwins xl =
(Tail, [])
|> List.foldBack
(fun cur (prev, acc) ->
match prev with
| Skip(prev) when prev - cur = 2 -> (Keep(cur), cur::prev::acc)
| Keep(prev) when prev - cur = 2 -> (Keep(cur), cur::acc)
| _ -> (Skip(cur), acc))
xl
|> snd
Here's another solution which uses a similar discriminate union strategy as my other answer but it works on sequences lazily so you can watch those twin (primes?) roll in as they come:
type status =
| KeepTwo of int * int
| KeepOne of int
| SkipOne of int
| Head
let filterTwins xl =
let xl' =
Seq.scan
(fun prev cur ->
match prev with
| KeepTwo(_,prev) | KeepOne prev when cur - prev = 2 ->
KeepOne cur
| SkipOne prev when cur - prev = 2 ->
KeepTwo(prev,cur)
| _ ->
SkipOne cur)
Head
xl
seq {
for x in xl' do
match x with
| KeepTwo(a,b) -> yield a; yield b
| KeepOne b -> yield b
| _ -> ()
}
for completeness sake, I'll answer this with what I eventually came up with, based on the friendly suggestions in this thread.
The benefits of this approach are that it doesn't need Seq.distinct, which I believe is an improvement as it allows for duplicates. However, it still needs List.rev which doesn't make it the fastest. Nor is it the most succinct code (see comparison of solution in question itself).
let filterTwins l =
l
|> Seq.pairwise
|> Seq.fold (fun a (x, y) ->
if y - x = 2 then (if List.head a = x then y::a else y::x::a)
else a) [0]
|> List.rev
|> List.tail

Resources