【问题标题】:f# concatenate list of objectsf# 连接对象列表
【发布时间】:2021-08-23 11:12:46
【问题描述】:
type Googol = {
    number : float
    power : float
    result : float
}

let generatePowers (n:float) : list<Googol> = 
    let rec powerInner (n:float) (p:float) (acc : list<Googol>) = 
        match n with
        | p when p <= 1.0 -> acc
        | p when p > 1.0 -> powerInner n (p-1.0) ([{ number=n; power=p; result=n**p}]@acc)
    let rec numberInner (n:float) (acc : list<Googol>) = 
        match n with 
        | n when n <=1.0 -> acc
        | n when n >1.0 -> numberInner (n-1.0) ((powerInner n [])@acc)
    numberInner n []

ProjectEuler.fsx(311,50): error FS0001: This expression was expected to have type
    'Googol list'
but here has type
    'Googol list -> Googol list'

我正在尝试解决这个问题-> https://projecteuler.net/problem=56 |但为此我需要生成低于 n [{ number=n; power=p; result=n**p}]@acc 这些列表我得到了上面的错误。请解释为什么错误说'Googol list -&gt; Googol list' 在函数中,是我将函数作为参数插入函数还是在连接后插入实际列表。 @ 是函数吗?

【问题讨论】:

  • 我只看了几秒钟,但注意到对 powerInner 的第二次调用得到了两个参数,而不是三个。应该是这样吗?
  • 是的,@ 连接两个列表。您第一次使用@ 是不必要的,因为您可以简单地使用{ number=n; power=p; result=n**p} :: acc,这是一个计算效率更高的运算符。它在 F# 列表的前面添加一个元素。
  • Bent Tranberg - 谢谢,不应该,无论如何ProjectEuler.fsx(385,28): error FS0003: This value is not a function and cannot be applied.
  • 并切换到 ::

标签: list recursion math f#


【解决方案1】:

这看起来像是家庭作业或练习,所以首先我会给出一些提示以继续前进。最后我会展示一个似乎可以工作的版本,然后告诉我如何解决这个问题。

任务是找到数字a ** b,对于小于100的ab,它自己的位数之和最大。

第一个问题是float不会给我们a ** b的所有数字,所以这个类型对解决问题没有用。为了解决这个问题,我们转向 BigInteger 类型和 BigInteger.Pow 函数。然后,如果我们运行以下 sn-p,我们会得到一个 1 后跟 200 个零,就像问题描述中所说的那样。

let x: bigint = BigInteger.Pow (100I, 100)
let x: string = string x
printfn "s=%s" x

要获得有用的结果,请更改 Googol 类型,使其使用 bigint,除了应该是 intpower

为什么powerInnernumberInner函数在函数generatePowers里面?这似乎没有特定目的,因此我建议将它们移出以使其更清晰。

函数powerInnern 进行匹配,但随后将结果命名为p,这会隐藏p 参数,使其未被使用。好的,这里的意图可能是匹配p 而不是n,所以只需修复它,然后p 参数的阴影就可以了。

首先在&lt;= 1 上进行测试,然后在&gt; 1 上进行测试会导致匹配不完整。如果第一行检查数字是否小于或等于 1,则在下一行中它必须大于 1。所以只需使用n -&gt; 而不使用when 来解决这个问题。我还怀疑你想测试 &lt;= 0 而不是 1。

这个

[{ number=n; power=p; result=n**p}]@acc

可以只是

{ number=n; power=p; result=n**p } :: acc

这里

(powerInner n [])

我怀疑你只需要一个功率的起始值,即 99

(powerInner n 99 [])

剧透警告

经过一番修改,这就是我最终得到的,它似乎打印出一个有用的数字列表。请注意,为了不通过打印输出运行所有 99 x 99 结果,我在这里使用了较低的起始数字 3 和 5 作为倒计时,所以我们可以得到一些简单的打印输出,我们可以研究分析。

type Googol = { number: bigint; power: int; result: bigint }

let rec powerInner (n: bigint) (p: int) (acc: Googol list) =
    match p with
    | p when p <= 0 -> acc
    | p ->
        let newNumber = { number = n; power = p; result = n ** p }
        printfn "newNumber=%0A" newNumber
        powerInner n (p - 1) (newNumber :: acc)

let rec numberInner (n: bigint) (acc: Googol list) =
    match n with
    | n when n <= 0I -> acc
    | n -> numberInner (n - 1I) ((powerInner n 5 []) @ acc)

let generatePowers (n: bigint) : Googol list =
    numberInner n []

let powers = generatePowers 3I

我不确定这个解决方案是否正确。反正我会做不同的。

我会简单地在两个循环中循环 a 和 b,一个在另一个循环中。对于每个a ** b,我会将结果转换为字符串,然后对字符串的数字求和。然后我会简单地使用一个可变的来保持最高的结果。使用其中一个花哨的 List 函数可以以更实用的方式实现相同的目的。

【讨论】:

    【解决方案2】:

    这里缺少一个参数:

    | n when n >1.0 -> numberInner (n-1.0) ((powerInner n [])@acc)
                                             ^^^^^^^^^^^^^^^
                                                   here
    

    powerInner 定义了三个参数,但你只传递了两个。

    在 F# 中,传递的参数少于定义的参数在技术上并不违法。如果你这样做,结果将是一个“预期”剩余参数的函数。例如:

    let f : int -> int -> string
    
    let x = f 42
    // Here, x : int -> string
    
    let y = x 5
    // Here, y : string
    

    因此,在您的情况下,省略最后一个参数会使结果类型 Googol list -&gt; Googol list,然后结果与运算符 @ 预期的类型 Googol list 不兼容。这是编译器在错误消息中告诉您的内容。

    【讨论】:

    • 不仅如此,第二个参数的类型错误。并且有几个警告。而且意图很难弄清楚。
    • 我只是在回答关于那个特定错误的问题。一个完整的仔细审查将花费比我愿意花费更多的时间。
    • 完全可以理解。后来很好奇,忍不住戳了戳,所以才发了一个答案。
    猜你喜欢
    • 1970-01-01
    • 2014-04-06
    • 1970-01-01
    • 2016-11-07
    • 2022-01-16
    • 2011-11-14
    • 2013-04-05
    • 2020-12-22
    • 1970-01-01
    相关资源
    最近更新 更多