简体   繁体   中英

f# get product of 4 adjacent numbers in an array

I'm trying to get the max product of 4 adjacent numbers in an array this is what I got now:

let max4 line =
    let rec loop acc = function
        |a :: b :: c :: [] -> acc
        |a :: b :: c :: d :: tl -> loop (max(acc, a*b*c*d)) tl
        |_ -> 0
    loop 0 line

I get a compilation error on the max(,) saying:

error FS0001: Type mismatch. Expecting a 'a but given a 'a * 'b -> 'a * 'b The resulting type would be infinite when unifying ''a' and ''a * 'b -> 'a * 'b'


Anybody knows what's wrong in this code ? (or another solution)

As an alternative to using explicit recursion, you could also solve this using existing F# library functions. This is how most F# data processing is written, but it is always good to learn how to write recursive functions by hand (because you sometimes need them).

So, just for completeness, here is a way to solve the probelm more declaratively using existing functions:

let max4 line = 
  line |> Seq.windowed 4 
       |> Seq.map (Seq.reduce (*))
       |> Seq.max

The first line turns the list into a sequence of 4-element arrays (windows). This is then passed to Seq.map that turns the window into a product of the elements. To do that, I'm using Seq.reduce which reduces sequence (window, in this case) using the specified function, here the (*) operator. Finally, to find the maximal element of the products, you can use Seq.max function.

Suppose the input is a list of integers:

let max4 line =
    let rec loop acc = function
        | x1::(x2::x3::x4::_ as xs) -> loop (max acc (x1*x2*x3*x4)) xs
        |_ -> acc
    loop System.Int32.MinValue line

You made some mistakes:

  • The built-in max function is in the curry form max: 'a -> 'a -> 'a .
  • The next case to address in your function should be b::c::d::tl , not tl only.
  • The product could be negative, so 0 is not a good starting point. Beware that integer overflow could happen (which I still haven't addressed in my function).

Both of the other answers sum sliding windows but in your question they're contiguous. If you want the latter you can define such a function:

let groupsOf n items =
  if n <= 0 then invalidArg "n" "must be greater than zero"
  if List.isEmpty items then invalidArg "items" "empty list"
  let rec loop i acc items =
    seq {
      match i, items with
      | 0, [] -> yield List.rev acc
      | _, [] -> ()
      | 0, _ ->
        yield List.rev acc
        yield! loop n [] items
      | _, x::xs -> yield! loop (i - 1) (x::acc) xs
    }
  loop n [] items

then use code similar to Tomas':

let max4 line = 
  line |> groupsOf 4 
       |> Seq.map (Seq.reduce (*))
       |> Seq.max

groupsOf ignores any partial group at the end (as does your code).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM