Как мне изменить эту функцию в ELM? - PullRequest
0 голосов
/ 08 июня 2018

Я пытаюсь подобрать функциональное программирование и решил начать с Задачи 1 в Project Euler: в основном добавить все числа, меньшие 1000, делимые на 3 или 5 (ссылка: ссылка ).

Это код, который я написал.Он выводит список факторов 3 или 5 (все еще нужно выяснить, как суммировать).

import Html exposing (text)
import Array

main =
  text (
toString
[findSum_maxZ 3 5 1000]
  )

findSum_maxZ x y max_z =
  Array.filter isDivisible_x_or_y (Array.initialize max_z identity)

isDivisible_x_or_y x = 
  if x % 3 == 0 || x % 5 == 0 then True else False

Моя проблема в том, что я ссылаюсь на 3 и 5 дважды, но я не могу вызвать isDivisible с дополнительными параметрамиболее абстрактные «х» и «у».Моя цель - определить эффективные методы удаления этих искусственно изменяемых значений, чтобы конечный пользователь мог изменять каждое входное значение только один раз.Любой совет?

Я прошу прощения, если этот вопрос глуп, не имеется много информации о ELM (особенно по сравнению с Python, C, C ++, Java и т. Д., Которые я использовал), и я до сих пор неполностью комфортно с функциональным программированием жаргона.Любая помощь приветствуется.

Ответы [ 2 ]

0 голосов
/ 08 июня 2018

Наиболее прямым ответом на ваш вопрос является то, что вы можете isDivisible_x_or_y взять два фактора, а затем использовать карри для передачи частично примененной функции в Array.filter.

То есть вы можете определитьisDivisible_x_or_y вот так (я также удалил синтаксис if True then True else False и просто возвращаю выражение напрямую):

isDivisible_x_or_y x y val =
    val % x == 0 || val % y == 0

Каррирование - это возможность предоставить только некоторые параметры функции и получить обратнофункция, которая принимает остальные параметры.Итак, определение типа isDivisible_x_or_y равно Int -> Int -> Int -> Bool (то есть принимает три Int значения и возвращает Bool).Если мы предоставим значения для аргументов x и y (например, isDivisible_x_y 3 5), мы получим функцию с определением типа Int -> Bool.Это тип, ожидаемый для Array.filter.

Рабочий пример можно посмотреть по адресу https://ellie -app.com / sdxWFL9ynka1

Еще пара замечаний:

List встречается гораздо чаще, чем Array в вязе.Вы можете использовать Array, только если вам нужно получить элементы по определенным индексам.Вместо Array.initialize вы можете использовать List.range

Использование оператора конвейера |> часто может сделать ваш код намного проще для чтения.Вместо text (toString (getValue)) у вас есть getValue |> toString |> text, который теперь находится в порядке выполнения операций и не имеет лишних скобок.Вся эта программа может быть одним простым конвейером (хотя во многих случаях помещение всего в один конвейер может быть чрезмерным):

main =
    List.range 0 max_z
        |> List.filter (isDivisible_x_or_y 3 5)
        |> toString
        |> text

isDivisible_x_or_y x y val =
    val % x == 0 || val % y == 0
0 голосов
/ 08 июня 2018

Самое замечательное в языках ML - то, что вы можете свободно создавать свой собственный «диалект» для решения проблем.

Вы можете использовать каррирование, чтобы применить только аргументы x и y кваша функция, создавая новую функцию, где заданные значения уже установлены.

import Html exposing (text)
import Array

main = [findSum 3 5 1000]
           |>toString
           |>text

findSum x y maxZ =
      let
         isDivisibleByX = isDivisible x
         isDivisibleByY = isDivisible y
      in
         Array.initialize maxZ identity
         |>Array.filter isDivisibleByX
         |>Array.filter isDivisibleByY
         --as you can see, it is possible to use a list instead of creating
         --new functions, it is up to you to check which abstraction works
         --the best


isDivisible a b =
      b % a == 0 

Вы также можете работать с одной функцией, не прибегая к каррированию:

import Html exposing (text)
import Array

main = [findSum 3 5 1000]
       |>toString
       |>text

findSum x y maxZ =
     Array.initialize maxZ identity
     |>Array.filter (\n-> isDivisible x n ) --or just (isDivisible x)
     |>Array.filter (\n-> isDivisible y n)


isDivisible a b =
  b % a == 0 

Если вы хотите отфильтровать массив всего одной строкой, вы можете сделать это:

import Html exposing (text)

main = findSum 3 5 1000
       |>toString
       |>text

findSum x y maxZ =
     let
        divisibles = \n-> isDivisible x n && isDivisible y n
     in
       List.range 0 maxZ 
       |>List.filter divisibles

isDivisible a b =
  b % a == 0 
...