F# 将循环转换为纯函数

F# 将循环转换为纯函数,f#,F#,我用c++为一个问题编写了以下代码: int sum = 0; for(int i =0; i < 1000; i++) { //Check if multiple of 3 but not multiple of 5 to prevent duplicate sum += i % 3 == 0 && i % 5 != 0 ? i: 0; //check for all multiple of 5, including those of 3

我用c++为一个问题编写了以下代码:

int sum = 0;

for(int i =0; i < 1000; i++)
{
    //Check if multiple of 3 but not multiple of 5 to prevent duplicate
    sum += i % 3 == 0 && i % 5 != 0 ? i: 0;
    //check for all multiple of 5, including those of 3
    sum += i % 5 == 0 ? i: 0;
}
    cout << sum;
我所拥有的可能完全是胡说八道……所以请帮帮我吧?

F#的功能远不止映射-这个问题建议使用过滤器和求和,我的方法是这样的

let valid n = Left as an exercise

let r =
    [1..1000]
    |> List.filter valid
    |> List.sum
printfn "%i" r
我不想做整个问题,但是填充缺少的函数应该不会太难

F#有很多函数,而不仅仅是map-这个问题建议使用filter和sum,我的方法是

let valid n = Left as an exercise

let r =
    [1..1000]
    |> List.filter valid
    |> List.sum
printfn "%i" r

我不想解决整个问题,但是填充缺少的函数应该不会太难

这就是如何将带有计数器的循环转换为递归函数的方法。您可以通过将累加器参数传递给保存当前循环计数的循环函数来实现这一点

例如:

let rec loop acc =
    if acc = 10 then
      printfn "endloop"
    else
      printfn "%d" acc
      loop (acc + 1)

loop 0

acc
为10时,此操作将停止。

这就是如何将带有计数器的循环转换为递归函数的方法。您可以通过将累加器参数传递给保存当前循环计数的循环函数来实现这一点

例如:

let rec loop acc =
    if acc = 10 then
      printfn "endloop"
    else
      printfn "%d" acc
      loop (acc + 1)

loop 0

acc
为10时,此操作将停止。

您的
sumList
功能是一个良好的开端。它已经(递归地)遍历了整个列表,因此您不需要将其包装在额外的
数组中。您只需扩展
sumList
,使其仅在与指定条件匹配时添加数字

下面是一个简化问题的解决方案-将所有可被3整除的数字相加:

open System

let rec sumList xs =
  match xs with
  | []    -> 0 // If the list is empty, the sum is zero
  | y::ys when y % 3 = 0 -> 
     // If the list starts with y that is divisible by 3, then we add 'y' to the
     // sum that we get by recursively processing the rest of the list
     y + sumList ys
  | y::ys -> 
     // This will only execute when y is not divisible by 3, so we just
     // recursively process the rest of the list and return 
     /// that (without adding current value)
     sumList ys

// For testing, let's sum all numbers divisble by 3 between 1 and 10.
let sum = sumList [ 1 .. 10 ]
这是使用显式递归编写函数的基本方法。实际上,jpalmer的解决方案也是我的解决方案,但是如果您正在学习F#,那么自己编写一些递归函数是很有用的

sashang提到的累加器参数是一种更高级的编写方法。如果要在大输入上运行函数,则需要这样做(这可能是Euler问题中的情况)。使用acculator参数时,可以使用尾部递归编写函数,因此即使在处理长列表时也可以避免堆栈溢出

基于累加器的版本的思想是,该函数接受额外的参数,该参数表示迄今为止计算的总和

let rec sumList xs sumSoFar = ...

最初调用它时,编写
sumList[…]0
。递归调用不会调用
y+sumList xs
,而是将
y
添加到累加器中,然后进行递归调用
sumList xs(y+sumSoFar)
。这样,F编译器就可以进行尾调用优化,将代码转换成一个循环(类似于C++版本)。

< p>您的代码> SUMLISTUCT/COD>函数是一个好的开始。它已经(递归地)遍历了整个列表,因此您不需要将其包装在额外的
数组中。您只需扩展
sumList
,使其仅在与指定条件匹配时添加数字

下面是一个简化问题的解决方案-将所有可被3整除的数字相加:

open System

let rec sumList xs =
  match xs with
  | []    -> 0 // If the list is empty, the sum is zero
  | y::ys when y % 3 = 0 -> 
     // If the list starts with y that is divisible by 3, then we add 'y' to the
     // sum that we get by recursively processing the rest of the list
     y + sumList ys
  | y::ys -> 
     // This will only execute when y is not divisible by 3, so we just
     // recursively process the rest of the list and return 
     /// that (without adding current value)
     sumList ys

// For testing, let's sum all numbers divisble by 3 between 1 and 10.
let sum = sumList [ 1 .. 10 ]
这是使用显式递归编写函数的基本方法。实际上,jpalmer的解决方案也是我的解决方案,但是如果您正在学习F#,那么自己编写一些递归函数是很有用的

sashang提到的累加器参数是一种更高级的编写方法。如果要在大输入上运行函数,则需要这样做(这可能是Euler问题中的情况)。使用acculator参数时,可以使用尾部递归编写函数,因此即使在处理长列表时也可以避免堆栈溢出

基于累加器的版本的思想是,该函数接受额外的参数,该参数表示迄今为止计算的总和

let rec sumList xs sumSoFar = ...

最初调用它时,编写
sumList[…]0
。递归调用不会调用
y+sumList xs
,而是将
y
添加到累加器中,然后进行递归调用
sumList xs(y+sumSoFar)
。这样,F编译器就可以进行尾调用优化,并将代码翻译成循环(类似于C++版本)。

< P>我不确定从命令式语言解决方案翻译是一种开发功能性思维工具的好方法(C++在您的案例中)已经定义了一种(迫切的)解决方案,所以最好还是坚持原来的问题支出

在掌握许多F#设施方面,来自的总体任务非常出色。例如,您可以使用下面代码段中的列表理解

// multipleOf3Or5 function definition is left for your exercise
let sumOfMultiples n =
    [ for x in 1 .. n do if multipleOf3Or5 x then yield x] |> List.sum
sumOfMultiples 999
或者,您也可以利用惰性,稍微概括一下@jpalmer建议的解决方案:

Seq.initInfinite id
|> Seq.filter multipleOf3Or5
|> Seq.takeWhile ((>) 1000)
|> Seq.sum
或者您甚至可以利用此机会掌握活动模式:

let (|DivisibleBy|_) divisior num = if num % divisor = 0 the Some(num) else None
{1..999}
|> Seq.map (fun i ->
    match i with | DivisibleBy 3 i -> i | DivisibleBy 5 i -> i | _ -> 0)
|> Seq.sum

上述三种变体都实现了一种通用模式,即使用所需属性生成一个成员序列,然后通过计算总和将其折叠。

我不确定从命令式语言解决方案进行翻译是否是开发函数式思维的好方法,因为工具(在您的例子中是C++)已经定义了(命令式)解决方案的方法,所以最好坚持原来的问题支出

在掌握许多F#设施方面,来自的总体任务非常出色。例如,您可以使用下面代码段中的列表理解

// multipleOf3Or5 function definition is left for your exercise
let sumOfMultiples n =
    [ for x in 1 .. n do if multipleOf3Or5 x then yield x] |> List.sum
sumOfMultiples 999
或者,您也可以利用惰性,稍微概括一下@jpalmer建议的解决方案:

Seq.initInfinite id
|> Seq.filter multipleOf3Or5
|> Seq.takeWhile ((>) 1000)
|> Seq.sum
或者您甚至可以利用此机会掌握活动模式:

let (|DivisibleBy|_) divisior num = if num % divisor = 0 the Some(num) else None
{1..999}
|> Seq.map (fun i ->
    match i with | DivisibleBy 3 i -> i | DivisibleBy 5 i -> i | _ -> 0)
|> Seq.sum

上述三种变体都实现了一种通用模式,即用所需属性生成一个成员序列,然后通过计算总和将其折叠。

:(伙计,我想我只需要一门函数式语言课程。谢谢你解释其他答案,这对我帮助很大。)(老兄,我想我只需要一门关于函数式语言的课程。谢谢你解释其他答案,这对我帮助很大。我想尝试用f#写作,因为这是一种思维方式的完全改变。刚刚发布了另一个变量