Recursion – Find the first duplicate value from the "circular" list with a recursive function

background

This year, I've taken up the challenge of the Advent of Code. The second challenge of the first day is to calculate a frequency for a particular device based on a series of numbers. Here is a quote from the challenge that describes the problem.

— Second part —
You notice that the device repeatedly repeats the same frequency change list. To calibrate the device, you need to determine the first frequency it will reach twice.

For example, if you use the same list of changes described above, the device will be looped as follows:

Current frequency 0, change from +1; resulting frequency 1.
Current frequency 1, change of -2; resulting frequency -1.
Current frequency -1, change of +3; resulting frequency 2.
Current frequency 2, change of +1; resulting frequency 3.
(At this point, the device continues at the top of the list.)
Current frequency 3, change of +1; resulting frequency 4.
Current frequency 4, change from -2; resulting frequency 2, which has already been seen.

In this example, the first frequency was reached twice. 2. Note that your device may need to repeat the list of frequency changes many times before a double frequency is found. You may find duplicates while editing the list.

Here are more examples:

+1, -1 first reaches 0 twice.
+3, +3, +4, -2, -4 first reaches 10 twice.
-6, +3, +8, +5, -6 first reaches 5 twice.
+7, +7, -2, -7, -4 first reaches 14 twice.

What is the first frequency that your device reaches twice?

solution

I decided to solve these challenges with elm, and because it is a functional programming language, the solution to this problem was a recursive function. This function is called calculateFirstDuplicatedFrequency. It is called first in main, with a large list of ints defined in another module and some other "empty values" to start it. The function is run through until the first doubled accumulated frequency is found. If there are at least two items in the list, the system checks whether the currently accumulated value is in the combination record. If so, the loop is stopped (so this is my base case).

If the current frequency is not included in this set, it recalculates First Duplicated Frequency with the original list, the end, the currently accumulated combos, and the current frequency.

If the function is called with a list of only one element, it will again execute calculateFirstDuplicatedFrequency, but will send the original list as input so that the loop restarts from the beginning and all the elements in the list are traversed again at the beginning.

If computeFirstDuplicatedFrequency is called with an empty list as frequencyModifiers, 0 is returned.

OBS. If you think that this snippet is a bit unreadable, you can watch my github instead

Import Set Exposure (Set)
Import HTML-Exposure (Html, Button, div, Text)
Import frequencies (frequency input)

main =
div []
    [ text "Second puzzle"
    , createText (calculateFirstDuplicatedFrequency frequencyInput frequencyInput Set.empty 0)
    ]







createText: Int -> Html msg
createText finalNumber =
div [] [ text (String.fromInt finalNumber) ]











calculateFirstDuplicatedFrequency: List Int -> List Int -> Set Int -> Int -> Int
calculateFirstDuplicatedFrequency originalFrequenciesfrequenciesModifiers frequencyCombos currentFrequency =
To let
createNewFreq = addFrequency currentFrequency
updateCombos = updateFrequencySet frequencyCombos
checkFrequencyCombos = checkCombos frequencyCombos
in the
Chassis Frequency Modifier of
      [] -> 0
      [x] ->
CalculateFirstDuplicatedFrequency originalFrequencies originalFrequencies (updateCombos (createNewFreq x)) (createNewFreq x)
(x :: xs) ->
if checkFrequencyCombos (createNewFreq x) then
createNewFreq x
otherwise
calculateFirstDuplicatedFrequency originalFrequencies xs (updateCombos (createNewFreq x)) (createNewFreq x)

addFrequency: Int -> Int -> Int
addFrequency currentFrequency frequency =
current Frequency + Frequency

updateFrequencySet: Set Int -> Int -> Set Int
updateFrequencySet frequencyCombos newFreq =
Set.insert newFreq frequencyCombos

checkCombos: Set Int -> Int -> Bool
checkCombos frequencyCombos frequency =
Set.Member frequency frequency combos

ask

I use a functional programming language for the first time and have so many questions about best practices! But my main concern with this code is:

  1. When I searched for recursive patterns, I could not find any recursion instances where the loop was restarted. Is my solution a good way to do this?
  2. Is it bad to send as many arguments as I am to calculate FirstDuplicatedFrequency? Is there a way to change this code to include fewer arguments? Must I?
  3. I used let in in the right way? It feels so nested and ugly. Are there any better ways to track all parameters?
  4. I think that the way I deal with the empty list case is not so good. I want to display an error message or otherwise say that this function does not work when called with an empty list. Do you have a suggestion how to handle it?