如何在 ELM 中重构此功能?
How do I refactor this function in ELM?
我正在尝试学习函数式编程,并决定从 Project Euler 的问题 1 开始:基本上将所有小于 1000 且能被 3 或 5 整除的数字相加 (link: 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 等相比)和我仍然对函数式编程术语不太满意。感谢您提供任何帮助。
ML 语言的妙处在于,您几乎可以自由地构建自己的 "dialect" 来解决问题。
您可以使用柯里化将 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
您问题的最直接答案是,您可以让 isDivisible_x_or_y
取两个因数,然后使用柯里化将部分应用的函数传递给 Array.filter
。
也就是说,你可以这样定义isDivisible_x_or_y
(我也删除了if True then True else False
语法,直接return表达式):
isDivisible_x_or_y x y val =
val % x == 0 || val % y == 0
柯里化是一种只向函数提供部分参数,并取回采用其余参数的函数的能力。所以,isDivisible_x_or_y
的类型定义是 Int -> Int -> Int -> Bool
(也就是说,它接受三个 Int
值和 return 一个 Bool
)。如果我们为 x
和 y
参数提供值(例如 isDivisible_x_y 3 5
),我们现在得到一个类型定义为 Int -> Bool
的函数。这是 Array.filter
.
期望的类型
您可以在 https://ellie-app.com/sdxWFL9ynka1
查看一个工作示例
另外几个注意事项:
List
在 Elm 中比 Array
更常见。如果您需要获取特定索引处的项目,您只会使用 Array
。您可以使用 List.range
而不是 Array.initialize
使用管道运算符 |>
通常可以使您的代码更易于阅读。而不是 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
我正在尝试学习函数式编程,并决定从 Project Euler 的问题 1 开始:基本上将所有小于 1000 且能被 3 或 5 整除的数字相加 (link: 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 等相比)和我仍然对函数式编程术语不太满意。感谢您提供任何帮助。
ML 语言的妙处在于,您几乎可以自由地构建自己的 "dialect" 来解决问题。
您可以使用柯里化将 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
您问题的最直接答案是,您可以让 isDivisible_x_or_y
取两个因数,然后使用柯里化将部分应用的函数传递给 Array.filter
。
也就是说,你可以这样定义isDivisible_x_or_y
(我也删除了if True then True else False
语法,直接return表达式):
isDivisible_x_or_y x y val =
val % x == 0 || val % y == 0
柯里化是一种只向函数提供部分参数,并取回采用其余参数的函数的能力。所以,isDivisible_x_or_y
的类型定义是 Int -> Int -> Int -> Bool
(也就是说,它接受三个 Int
值和 return 一个 Bool
)。如果我们为 x
和 y
参数提供值(例如 isDivisible_x_y 3 5
),我们现在得到一个类型定义为 Int -> Bool
的函数。这是 Array.filter
.
您可以在 https://ellie-app.com/sdxWFL9ynka1
查看一个工作示例另外几个注意事项:
List
在 Elm 中比 Array
更常见。如果您需要获取特定索引处的项目,您只会使用 Array
。您可以使用 List.range
Array.initialize
使用管道运算符 |>
通常可以使您的代码更易于阅读。而不是 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