是否有可能在Haskell中推广方程?

时间:2015-11-01 00:05:42

标签: haskell

对于我对问题的不良措辞表示歉意。我试过寻找答案,但不知道要搜索什么使得很难找到答案。

这是一个计算三角形面积的简单函数。

triangleArea        :: Float -> Float -> Float -> Float
triangleArea a b c 
    | (a + b) <= c  = error "Not a triangle!"
    | (a + c) <= b  = error "Not a triangle!"
    | (b + c) <= a  = error "Not a triangle!"
    | otherwise     = sqrt (s * (s - a) * (s - b) * (s - c))
        where s     = (a + b + c) / 2

为了进行错误检查,已经使用了三行函数。我想知道这三条线是否可以压缩成一条通用线。

我想知道是否可能出现类似以下的内容

(arg1 + arg2) == arg3

Haskell知道检查三个参数的每个可能组合。

3 个答案:

答案 0 :(得分:5)

我认为@ behzad.nouri的评论是最好的。有时候做一点数学是最好的编程方式。这对@ melpomene的解决方案来说有点过头了,我认为这个解决方案很有趣。让我们编写一个类似permutations的函数,但计算组合:

import Control.Arrow (first, second)

-- choose n xs returns a list of tuples, the first component of each having
-- n elements and the second component having the rest, in all combinations
-- (ignoring order within the lists). N.B. this would be faster if implemented
-- using a DList.
choose :: Int -> [a] -> [([a],[a])]
choose 0 xs = [([], xs)]
choose _ [] = []
choose n (x:xs) =
  map (first (x:)) (choose (n-1) xs) ++
  map (second (x:)) (choose n xs)

所以..

ghci> choose 2 [1,2,3]
[([1,2],[3]),([1,3],[2]),([2,3],[1])]

现在你可以写

triangleArea a b c
  | or [ x + y <= z | ([x,y], [z]) <- choose 2 [a,b,c] ] = error ...

答案 1 :(得分:3)

这并没有解决如何缩短错误检查代码的问题,但是您可以通过使用不变量定义一些新类型来限制重复它的频率。此函数需要进行错误检查,因为您无法信任用户提供构成合理三角形的NSString *atributeValue =@“Harry”; NSPredicate *predicate = [NSPredicate predicateWithFormat:@“deleted == %@ && inactive == %@ && name begindsWith %@ ”, @0,@0,atributeValue]; NSSortDescriptor *descriptor1 = [nssortDescriptor sortDescriptorWithKey:@“accounts”ascending : NO]; NSSortDescriptor *descriptor2 = [nssortDescriptor sortDescriptorWithKey:@“sales”ascending : NO]; NSArray *result = [all filteredArrayUsingPredicate: predicate]sortedArrayUsingDescriptors :[NSArray arrayEithObjects:descriptor1,descriptor2]; 三元组,如果您继续以这种方式定义函数,那么您编写的每个与三角形相关的函数都需要类似的错误检查。

但是,如果您定义了三角形类型,则只能在创建三角形时检查不变量,然后保证所有其他函数都能接收到有效三角形:

Float

这里我们导出Triangle类型,但不导出它的构造函数,因此客户端必须使用module Triangle (Triangle(), mkTriangle, area) where data Triangle a = Triangle a a a deriving Show mkTriangle :: (Num a, Ord a) => a -> a -> a -> Either String (Triangle a) mkTriangle a b c | a + b <= c = wrong | a + c <= b = wrong | b + c <= a = wrong | otherwise = Right $ Triangle a b c where wrong = Left "Not a triangle!" area :: Floating a => Triangle a -> a area (Triangle a b c) = sqrt (s * (s - a) * (s - b) * (s - c)) where s = (a + b + c) / 2 代替,这可以执行所需的错误检查。然后mkTriangle以及您编写的任何其他三角函数都可以省略它们接收有效三角形的检查。这种一般模式被称为“智能构造函数”。

答案 2 :(得分:2)

这是两个想法。

  1. 使用现有工具,您可以生成参数的所有排列并检查它们是否满足条件。因此:

    import Data.List
    triangleArea a b c
        | any (\[x, y, z] -> x + y <= z) (permutations [a,b,c])
                    = error "Not a triangle!"
        | otherwise = {- ... -}
    

    这并不需要编写额外的代码;但是,它会搜索一些你不关心的排列。

  2. 使用通常的技巧从列表和左侧选择元素。 zippers函数是我经常使用的函数:

    zippers :: [a] -> [([a], a, [a])]
    zippers = go [] where
        go b [] = []
        go b (v:e) = (b, v, e) : go (v:b) e
    

    我们可以使用它来构建一个只选择适当的元素三元组的函数:

    triples :: [a] -> [(a, a, a)]
    triples xs = do
        (b1, v1, e1) <- zippers xs
        (b2, v2, e2) <- zippers e1
        v3 <- b1 ++ b2 ++ e2
        return (v1, v2, v3)
    

    现在我们可以像第(1)部分一样编写我们的警卫,但它只考虑添加的唯一配对。

    triangleArea a b c
        | any (\(x, y, z) -> x + y <= z) (triples [a,b,c])
                    = error "Not a triangle!"
        | otherwise = {- ... -}
    
相关问题