我有一个问题,关于应用 DRY 原则的特定方式是否被认为是 Haskell 中的良好实践。我将举一个例子,然后询问我所采用的方法是否被认为是良好的 Haskell 风格。简而言之,问题是这样的:当你有一个很长的公式,然后你发现自己需要在其他地方重复该公式的一些小子集时,你是否总是将重复的公式子集放入一个变量中,这样你就可以保持 DRY ? 为什么或者为什么不?
示例: 假设我们正在获取一串数字,并将该字符串转换为其对应的 Int 值。(顺便说一句,这是来自“Real World Haskell”的练习)。
这是一个有效的解决方案,只是它忽略了边缘情况:
asInt_fold string = fst (foldr helper (0,0) string)
where
helper char (sum,place) = (newValue, newPlace)
where
newValue = (10 ^ place) * (digitToInt char) + sum
newPlace = place + 1
它使用 foldr,累加器是下一个位置值和到目前为止的总和的元组。
到现在为止还挺好。现在,当我去实现边缘情况检查时,我发现我需要在不同的地方使用一小部分“newValue”公式来检查错误。例如,在我的机器上,如果输入大于 (2^31 - 1),则会出现 Int 溢出,因此我可以处理的最大值为 2,147,483,647。因此,我进行了 2 次检查:
- 如果位值 9(十亿位)且数字值 > 2,则存在错误。
- 如果 sum + (10 ^ place) * (digitToInt char) > maxInt,则有错误。
这 2 项检查使我重复了部分公式,因此我引入了以下新变量:
- digitValue = digitToInt 字符
- newPlaceComponent = (10^place) * digitValue
我引入这些变量的原因仅仅是 DRY 原则的自动应用:我发现自己重复了公式的那些部分,所以我定义了它们一次且仅一次。
但是,我想知道这是否被认为是好的 Haskell 风格。有明显的优点,但我也看到了缺点。它确实使代码更长,而我见过的大部分 Haskell 代码都非常简洁。
那么,您是否考虑过这种良好的 Haskell 风格,您是否遵循这种做法?为什么/为什么不?
对于它的价值,这是我处理许多边缘情况的最终解决方案,因此具有相当大的 where 块。由于我应用了 DRY 原则,您可以看到块有多大。
谢谢。
asInt_fold "" = error "You can't be giving me an empty string now"
asInt_fold "-" = error "I need a little more than just a dash"
asInt_fold string | isInfixOf "." string = error "I can't handle decimal points"
asInt_fold ('-':xs) = -1 * (asInt_fold xs)
asInt_fold string = fst (foldr helper (0,0) string)
where
helper char (sum,place) | place == 9 && digitValue > 2 = throwMaxIntError
| maxInt - sum < newPlaceComponent = throwMaxIntError
| otherwise = (newValue, newPlace)
where
digitValue = (digitToInt char)
placeMultiplier = (10 ^ place)
newPlaceComponent = placeMultiplier * digitValue
newValue = newPlaceComponent + sum
newPlace = place + 1
maxInt = 2147483647
throwMaxIntError =
error "The value is larger than max, which is 2147483647"