【问题标题】:Adding two functions together in Haskell在 Haskell 中将两个函数相加
【发布时间】:2018-04-12 07:48:20
【问题描述】:

您好,我是 Haskell 的新手,遇到了一个有趣的问题,但我不确定如何解决它。我将仅向您展示问题的两个部分作为示例。

问题是我们要输入一个介于 13 到 15 位之间的数字。 然后从该数字中删除最后一个数字。例如 19283828382133 应该输出完全相同的数字,只是没有最后的 3 1928382838213。

然后这些数字中的每个奇数(不是数字)都将加倍。所以你会得到 2,9,4,8​​,6 等

到目前为止,这是我的代码。正如您从代码中看到的那样,我已经能够单独完成这两个部分(工作),但我不确定如何将它们加在一起。

lastdigit :: Integer -> Integer -- This Function removes the last number
lastdigit x = x`div`10

doubleOdd (x:xs) = (2*x):(doubleEven xs) -- This function doubles every odd digit not number.
doubleOdd [] = []
doubleEven (x:xs) = x:(doubleOdd xs)
doubleEven [] = []

因此,为了进一步解释我正在尝试构建的程序,我将首先完成输入 13 到 15 位数字的步骤。然后它将首先删除最后一个数字,然后自动进入下一步将每个奇数(不是数字)加倍。谢谢

【问题讨论】:

  • 但是如果奇数位大于等于5,那会怎样呢?此后比“新”位数大于 10。
  • 这实际上是问题的下一部分。其中,如果你得到 10,它就会变成 1,因为 10 是 1 + 0,这 = 1。如果你得到类似 14 的东西,它就会变成 5,因为 1 + 4 = 5

标签: haskell


【解决方案1】:

首先,您需要一种将大数分解为数字的方法。

digits :: Integral x => x -> [x]
digits 0 = []
digits x = digits (x `div` 10) ++ [x `mod` 10]

这给了你...

Prelude> digits 12345
[1,2,3,4,5]

然后您可以使用init 删除最后一位数字

Prelude> (init . digits) 12345
[1,2,3,4]

map 处理列表中奇数元素的辅助函数。

mapOdd _ [] = []
mapOdd f (x:[]) = [f x]
mapOdd f (x:y:rest) = f x : y : mapOdd f rest

给你...

Prelude> mapOdd (+10) [1..10]
[11,2,13,4,15,6,17,8,19,10]

还有一个返回大数的函数……

undigits = sum . zipWith (*) [10^n | n <- [0..]] . reverse

导致...

Prelude> undigits [1, 2, 3, 4]
1234

把它们放在一起

Prelude> undigits . mapOdd (*2) . init . digits $ 12345
2264

尤其是在函数式语言中,总是尝试通过组合解决小问题的方法来解决问题:)

【讨论】:

  • 你写的很多代码都是直接写在前奏曲中的谢谢
  • 在最终解决方案中sumDigits . mapOdd (*2) . init . digits 3/4 函数没有在Prelude 中定义。我假设您的意思是答案是在 ghci 会话的上下文中呈现的,而不是完整的模块。如果你想要一个模块,你需要的一切都在那里。
  • 不应该digits 0[0]吗?
  • @Elmex80s 为了简单起见,考虑到问题,我认为忽略这种情况是合理的。但你是对的。要解决这个问题,您要么需要定义一个包装函数来处理这种情况,要么回退到 hacky 字符串拆分解决方案:)
  • @JordanMackie 如果您正在考虑处理这些边缘情况,可能最好将数字实现为对每个数字进行控制,然后在包装函数中反转结果(digits' 0 = []; digits' x = let (d, m) = x divMod` 10 in m : digits d ` 和 digits 0 = ['0']; digits x = reverse . digits' $ x)
【解决方案2】:

缺少的组件是一种将整数分解为数字并从那里重新构建的方法。这很简单:

digits:: Int -> [Int]
digits = map (`mod` 10) . takeWhile (/= 0) . iterate (`div` 10)

undigits :: [Int] -> Int
undigits = foldr f 0 where f i r = 10 * r + i

然后看起来您需要以两种不同的方式对这些数字进行后处理,但前提是它们与谓词匹配。让我们为此构建一个组合器:

when :: (a -> Bool) -> (a -> a) -> a -> a
when p f a = if p a then f a else a

当您想在奇数位置(从左到右)输入两位数时,会出现第一种情况。再次微不足道,digits 通过增加 10 的幂来分解数字的小不便。让我们在每个数字前面加上它的位置:

prefix :: [Int] -> [(Int, Int)]
prefix is = let n = length is in zip [n, n-1..1] is

doubleOdd 现在可以表示为

doubleodd :: [Int] -> [Int]
doubleodd = map (snd . when (odd . fst) (id *** double)) . prefix

您在评论中提到,当双数溢出时,必须将其数字相加。这是我提到的第二种情况,也是简单本身:

double :: Int -> Int
double = when (>= 10) (sum . digits) . (* 2)

这是你的最终程序:

program = undigits . doubleodd . tail . digits

...假设“13 到 15 位之间”部分单独验证。

【讨论】:

    【解决方案3】:

    我希望这会有所帮助,并意识到它可以被清理很多。列表索引以 0 开头,它也是一个偶数,也是列表的第一个元素。列表理解处理 0,2,4 ...第 1,2,3 项。

    let f n = [mod n 10] ++ f (div n 10)
    let r = [if even i then d*2 else d|(i,d)<-zip [0..] (init.reverse.take 14.f$19283828382133)]
    sum [b*(10^a)|(a,b) <- zip [12,11..0] r]
    

    2948684868416

    如果你想让它处理任何长度的数字,这里最简单的方法是length $ show 19283828382133,但我确实有一个函数可以做到这一点。在 3 个位置使用长度作为值,一次在组合中的take 函数中使用完整值。

    【讨论】:

      猜你喜欢
      • 2019-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-18
      相关资源
      最近更新 更多