Advent of Code 2018 day 1 part 2: find the first aggregated number that occures twice I am using the coding puzzels of https://adventofcode.com for improving my F# skills.

Problem:

Day 1 Part 2:

Starting with frequency 0, a list of numbers should be added successive generating new frequencies. Searched is the first frequency which occurs twice. If the end of the list has been reached without success; the list should be processed again.


  Example Input: [+1, -2, +3, +1]
  
  
  Current frequency  0, change of +1; resulting frequency  1.
  Current frequency  1, change of -2; resulting frequency -1.
  C* urrent frequency -1, change of +3; resulting frequency  2.
  Current frequency  2, change of +1; resulting frequency  3.
  (At this point, we continue from the start of the list.)
  Current frequency  3, change of +1; resulting frequency  4.
  Current frequency  4, change of -2; resulting frequency  2, which has already been seen.
  
  
  In this example, the first frequency reached twice is 2.


Solution

let input = [1;-2;3;1]

type State =
    | FinalState of int
    | IntermediateState of int * int Set

type State with
    static member Zero = IntermediateState(0, Set.empty.Add(0)) 

let foldIntermediateState (freqHistory: int Set, freq : int) : State =
    let isFinal = freqHistory |&gt; Set.contains freq
    if isFinal then
        FinalState (freq)
    else
        IntermediateState (freq, freqHistory.Add(freq))

let foldState state value =
    match state with
        | FinalState _ -&gt; state
        | IntermediateState (lastFreq, freqHistory) -&gt; foldIntermediateState (freqHistory, lastFreq + value)

let rec processListRec state lst =
    let result = lst |&gt; Seq.fold foldState state
    match result with
        | FinalState result -&gt; printfn "The result is: %i" result
        | IntermediateState _ -&gt; lst |&gt; processListRec result

input |&gt; Seq.map int |&gt; processListRec State.Zero


Questions

In fact, I am proud to have found a solution without mutable states that looks (at least for me) functional! :D. So any feeback that helps improving the solution or even alternative function approches for solving that problem are welcome :).
It also looks functional to me, and I like your use of union type State.



You can define the Zero static member of State as:

type State =
    | FinalState of int
    | IntermediateState of int * int Set
    static member Zero = IntermediateState(0, Set.empty.Add(0)) 


You don't have to create a separate with-definition.



In general I avoid writing the result to the console in the algorithm function it self. Instead it is more useful to return the result and let the caller handle that as needed:

let rec processListRec state lst =
    let result = lst |&gt; Seq.fold foldState state
    match result with
    | FinalState result -&gt; result
    | IntermediateState _ -&gt; lst |&gt; processListRec result


Used as:

printfn "%A" (processListRec State.Zero input)




You need some kind of stop condition or an input check if all changes are positive or negative.

For instance will [1;1;1] run forever, because no frequency will ever occur twice.



A raw recursive solution could be:

let findReoccurantFrequency input = 
    if (input |&gt; List.groupBy (fun x -&gt; x &gt; 0)) |&gt; List.length &lt; 2 then 
        raise (Exception "Invalid input (only positive or negative changes)")
    else
        let rec runner lst freq freqs =
            if freqs |&gt; List.contains freq then
                freq
            else
                let head, tail = if lst = [] then input.Head, input.Tail else lst.Head, lst.Tail
                runner tail (freq + head) (freq::freqs)
        runner input 0 []


I'm not saying that it's a better solution, just another approach for the exercise and inspiration. Especially I'm not proud of the input check.



Edit

As pointed out by JanDotNet in his comment, my input validation is poor, so it's maybe better to leave it out. Below is a revised version of my implementation using a Set instead of a List for the frequencies:

let findReoccurantFrequency input = 
    let rec runner lst freq freqs =
        match freqs |&gt; Set.contains freq with
        | true -&gt; freq
        | false -&gt;
            let head, tail = if lst = [] then input |&gt; List.head, input |&gt; List.tail else lst.Head, lst.Tail
            runner tail (freq + head) (freqs.Add(freq))
    runner input 0 Set.empty