DEV Community

Discussion on: Write a script to find "Happy Numbers"

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

Elm solution which does not use strings. Hopefully quite understandable.

sumDigitSquares sum num =
  if num == 0 then -- exit condition, processed all digits
    sum
  else
    let
      digit = rem num 10 -- get right-most digit
      square = digit * digit
      newNum = num // 10 -- remove right-most digit
    in
      sumDigitSquares (sum + square) newNum


isHappy i =
  if 0 <= i && i <= 9 then -- exit condition, single digit number
    i == 1 || i == 7
  else
    let sum = sumDigitSquares 0 i
    in isHappy sum

Two recursive functions. Exit conditions are getting a number between 0 and 9. Zero is impossible to actually get from other numbers, but there if someone input 0. The only happy numbers in that range are 1 and 7. Should work with negative numbers too.


Here is a runnable solution with UI to test a range of numbers. Try it at elm-lang.org/try.

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Task
import Result


sumDigitSquares sum num =
  if num == 0 then
    sum
  else
    let
      digit = rem num 10
      square = digit * digit
      newNum = num // 10
    in
      sumDigitSquares (sum + square) newNum


isHappy i =
  if 0 <= i && i <= 9 then
    i == 1 || i == 7
  else
    let sum = sumDigitSquares 0 i
    in isHappy sum


calculateUpTo i =
  List.range 1 i
    |> List.filter isHappy
    |> CalculateCompleted i


-- UI


type Msg =
  NumberInput String
  | Calculate Int
  | CalculateCompleted Int (List Int)


type Calculation =
  NotStarted
  | Calculating Int
  | Calculated Int (List Int)


type alias Model =
  { userInput : String
  , validatedNumber : Result String Int
  , calculation : Calculation
  }


init =
  { userInput = ""
  , validatedNumber = Err ""
  , calculation = NotStarted
  } ! []


update msg model =
  case msg of
    NumberInput s ->
      { model
        | userInput = s
        , validatedNumber = String.toInt s
      } ! []

    Calculate i ->
      { model | calculation = Calculating i }
        ! [ Task.perform calculateUpTo (Task.succeed i) ]

    CalculateCompleted i list ->
      { model | calculation = Calculated i list } ! []


view model =
  div []
    [ div [] [ text "Check for happy numbers up to" ]
    , div [] [ input [type_ "text", onInput NumberInput, value model.userInput ] [] ]
    , div [] 
      [ case model.validatedNumber of
          Err err ->
            button [ type_ "button", disabled True ] [ text "Calculate" ]
          Ok i ->
            button [ type_ "button", onClick (Calculate i) ] [ text "Calculate" ]
      ]
    , case model.calculation of
        NotStarted ->
          text ""

        Calculating i ->
          div [] [ hr [] [], text ("Calculating up to " ++ toString i) ]

        Calculated i list ->
          div []
            ( [ hr [] [], div [] [ text ("Happy numbers up to " ++ toString i) ] ]
              ++ List.map (\num -> div [] [ text (toString num) ]) list
            )

    ]


main =
  Html.program
    { init = init
    , view = view
    , update = update
    , subscriptions = \_ -> Sub.none
    }