如何在ELM中重构此功能?

时间:2018-06-07 21:52:50

标签: functional-programming elm

我正在尝试选择函数式编程,并决定从Project Euler上的问题1开始:基本上将所有小于1000的数字加上3或5整除(链接:a link)。

这是我写的代码。它输出一个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两次但是我不能用更抽象的'x'和'y'的附加参数调用isDivisible。我的目标是确定删除这些人为可变值的有效方法,以便最终用户只需修改一次输入值。有什么建议吗?

我很抱歉,如果这个问题很愚蠢,没有很多关于ELM的信息(特别是与我使用过的python,c,c ++,java等相比),我仍然对功能编程不太满意行话。任何和所有的帮助表示赞赏。

2 个答案:

答案 0 :(得分:5)

关于ML语言的一个很酷的事情是你几乎可以自由地建立自己的“方言”来解决问题。

您可以使用currying仅将xy参数应用于函数,创建一个新函数,其中已经设置了提供的值。

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 

您也可以使用单一功能,而无需使用currying:

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 

答案 1 :(得分:2)

您问题的最直接答案是,您可以isDivisible_x_or_y取两个因素,然后使用currying将部分应用的函数传递给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

Currying是仅向函数提供一些参数的能力,并返回一个接受其余参数的函数。因此,isDivisible_x_or_y的类型定义为Int -> Int -> Int -> Bool(即,它需要三个Int值并返回Bool)。如果我们提供xy参数的值(例如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