3

我想用 Haskell 来解决一个金融组合问题,list monad 似乎很适合这个。

现在,我对 list monad 的问题是它无法为所涉及的值命名。我将尝试举例说明:

loan = [1000*x | x <- [1..3]]
interest_rate = [0.005*x | x <- [4..10]]

calc = do                                                                                    
  l <- loan                                                                                  
  i <- interest_rate                                                                         
  return (l*i)

上面运行 calc 给了我一个数字列表 ( [20.0,25.0,30.0,35.0,40.0, ... ]),但我不知道每次计算使用的贷款和利率。

我在这里迷路了,我的直觉告诉我要创建我自己的一元类型,说HelpfulNumber :: (String,[Double])并且以某种方式说:

>>=return应该>>= . snd是和return . snd

我在这里是正确的路线,还是有更好的方法?老实说,我感到有点失落。

4

3 回答 3

6

您可以使用记录类型使输出更清晰:

data Loan = Loan {final :: Double, 
                  rate  :: Double, 
                  loan  :: Integer, 
                  years :: Int}
   deriving Show

printloans :: [Loan] -> IO()
printloans = mapM_ print

在 ghci 提示符下使用printloans loans或。printloans loans'

编辑:我忘了包括dp. 它用于四舍五入到给定的小数位数:

dp :: Int -> Double -> Double
n `dp` a = (/ 10.0^n).fromInteger.round.(* 10.0^n) $ a

这是一种直接使用列表的方法:

loans = [Loan {final = (2 `dp`) $ fromInteger amt*(1+ir)^yrs, 
               rate  = ir,
               loan  = amt,
               years = yrs}  
        | ir <- [0.005*x | x <- [4..10]],
          amt <- [1000*x | x <- [1..3]],
          yrs <- [1..4]
        ]

但如果你喜欢 monadic 风格,你可以使用:

loans' = do
  ir <- [0.005*x | x <- [4..10]]
  amt <- [1000*x | x <- [1..3]]
  yrs <- [1..4]
  return Loan {final = (2 `dp`) $ fromInteger amt*(1+ir)^yrs, 
               rate  = ir,
               loan  = amt,
               years = yrs}

这得益于更少的逗号,并且更容易更改<-行的顺序以更改答案的顺序。您可以在Loan记录中添加额外内容并使用它们进行计算。你会得到这样的输出:

*Main> printloans loans'
Loan {final = 1020.0, rate = 2.0e-2, loan = 1000, years = 1}
Loan {final = 1040.4, rate = 2.0e-2, loan = 1000, years = 2}
Loan {final = 1061.21, rate = 2.0e-2, loan = 1000, years = 3}
Loan {final = 1082.43, rate = 2.0e-2, loan = 1000, years = 4}
Loan {final = 2040.0, rate = 2.0e-2, loan = 2000, years = 1}
Loan {final = 2080.8, rate = 2.0e-2, loan = 2000, years = 2}
...
...

编辑:

你在别处告诉我你想要输出像ir_5% yrs_3 amt_4000 tot_4360.5. 它更丑陋,但这是一种做这种事情的方法:

loans'' = do
  ir <- [0.005*x | x <- [4..10]]
  amt <- [1000*x | x <- [1..3]]
  yrs <- [1..4]
  let final = (2 `dp`) $ fromInteger amt*(1+ir)^yrs
  return $ "final_" ++ show final
        ++ ",  ir_" ++ show ((2 `dp`) $ ir*100.0)    -- rounded away a rounding error in 3.5% 
        ++ "%,  amt_" ++ show amt 
        ++ ",  yrs_" ++ show yrs

当你这样做时,mapM_ putStrLn loans''你会得到像

final_1020.0,  ir_2.0%,  amt_1000,  yrs_1
final_1040.4,  ir_2.0%,  amt_1000,  yrs_2
final_1061.21,  ir_2.0%,  amt_1000,  yrs_3
final_1082.43,  ir_2.0%,  amt_1000,  yrs_4
final_2040.0,  ir_2.0%,  amt_2000,  yrs_1
....

但我认为记录类型好得多——它的输出更容易阅读,而且对字符串的干扰更少。

于 2012-08-25T15:24:57.860 回答
2

你为什么不做两个辅助函数呢?

getName (loan, rate) = "loan="++loan++"&rate="++rate

getAnswer (loan, rate) = loan*rate

然后使用列表理解

loans = [1000*x | x <- [1..3]]
interest_rates = [0.005*x | x <- [4..10]]

input_tuples = [(l, i) | l<-loans, i<-interest_rates]]

answers = [(getName t, getAnswer t) | t<-input_tuples]]

不需要 monad,甚至不需要列出 monad。

于 2012-08-25T14:40:40.217 回答
1

我不知道你在这里寻找什么,因为你会如何给利率命名?

但是您当然可以将利率与最终结果一起存储:

calc = do                                                                                    
  l <- loan                                                                                  
  i <- interest_rate                                                                         
  return (i, l*i)
-- Yields: [(0.02, 20.0), (0.025, 25.0), ...]
于 2012-08-25T13:31:30.330 回答