2012-12-08 44 views
5

我需要编写一个将以递归方式生成钻石输出的Haskell程序。 这里是给定的输入输出示例
使用Haskell打印钻石图案

输入:1
输出:

* 
* * 
* 

输入:2
输出:

* 
    * * 
    * 
*  * 
* * * * 
*  * 
    * 
    * * 
    * 

输入:3
输出:

   *    
      * *    
      *    
      *  *   
     * * * *   
      *  *   
      *    
      * *    
      *    

    *     *  
    * *    * * 
    *     *  
*  *   *  * 
* * * *   * * * * 
*  *   *  * 
    *     *  
    * *    * * 
    *     *  
      *    
      * *    
      *    
      *  *   
     * * * *   
      *  *   
      *    
      * *    
      *  

我写了以下功能:

next 0 = [1,0,1] 
next n = map (+3^n) (next (n-1)) ++ next (n-1) ++ map (+3^n) (next (n-1)) 
lpad n = map (++"*") (zipWith ($) (map (take)(next (n-1))) ((repeat(repeat ' ')))) 
pretty n = putStrLn $ intercalate "\n" $ lpad n 

这给下面的输出:

漂亮1

* 
* 
* 

漂亮的2

* 
    * 
    * 
* 
* 
* 
    * 
    * 
    * 

谁能帮助我的另一半?提前致谢。

+0

这被要求在PUCSD考试。 (浦那大学计算机科学系)。 – 2012-12-10 18:33:43

+0

它是我的主人。 – 2012-12-10 18:36:59

回答

4

对于n==0,next n描述了整个图片直到镜像。对于更大的n,情况并非如此。因此,在第一步中,我们改变了next功能输出对称图片:

mmap = map . map 

next :: Int -> [[Int]] 
next 0 = [[1],[0,2],[1]] 
next n = sn ++ map (\a -> a ++ map (+2*3^n) a) nn ++ sn 
    where 
    nn = next (n - 1) 
    sn = mmap (+3^n) nn 

现在,next n描述了所有明星的位置。为了打印它们,我们首先计算相对距离。

diffs :: [Int] -> [Int] 
diffs (x:xs) = x: diffs' x (xs) 
    where 
    diffs' x (y:ys) = y - x - 1 : diffs' y ys 
    diffs' _ [] = [] 
diffs [] = [] 

lpad :: Int -> [[Char]] 
lpad = map (concatMap $ \n -> replicate n ' ' ++ "*") . map diffs . next' 

适用于一条线,diffs返回的,我们需要各路明星之前付诸表决,并lpad从生成画面的空格数的列表。像以前一样打印:

pretty :: Int -> IO() 
pretty n = putStrLn $ unlines $ lpad n 
+0

这是一个很好的解决问题的方法,值得赞赏! – AndrewC

5

我喜欢这个任务,所以我写了一个替代解决方案。

我们可以建立它,有点像使用漂亮的打印机。看看pretty包,以采取这些想法并正确使用它们,但让我们坚持原来的[String]为此。

首先,让我们做一个空的网格

blank :: Int -> [String] 
blank n = replicate (3^n) $ replicate (3^n) ' ' 

然后让我们定义的钻石。

diamond :: Int -> [String] 
diamond 0 = ["*"] 
diamond n = let 
     o = diamond (n-1) 
     x = blank (n-1) in 
    joinMatrix [[x,o,x] 
       ,[o,x,o] 
       ,[x,o,x]] 

但是我们怎样才能将[String]这个矩阵加在一起呢? 首先获得所有String s表示,应彼此相邻,而不是在对方使用transpose串联在一起,然后concat他们都:

joinLine :: [[String]] -> [String] 
joinLine = map concat.transpose 

要做到这一点给整个矩阵,我们需要在加入行每一行,然后Concat的所有的线连成线中的一个列表:

joinMatrix :: [[[String]]] -> [String] 
joinMatrix = concat.map joinLine 

打印辅助功能:

put = mapM_ putStrLn 
d n = put $ diamond n 

你可能会争辩说,数值解决方案更高效,而且它是,但是d 4是适合我的屏幕并且速度不是很慢的。你也可以争辩说这个解决方案更清晰。

*Main> d 0 
* 
*Main> d 1 
* 
* * 
* 
*Main> d 2 
    *  
    * * 
    *  
*  * 
* * * * 
*  * 
    *  
    * * 
    *  

(它适用于高N过,但他们会做这个职位不必要的长时间在页面上。)

+0

不要忘记投票cebewee的[好回答](http://stackoverflow.com/questions/13778632/print-diamond-pattern-using-haskell/13779048#13779048)! – AndrewC

+0

谢谢:)我非常喜欢你的方法,你只需要编码一次钻石,而在我的方法中,有两颗钻石:一次在'下一个0',一次在'下一个n'。 –

1

这是从AndrewC的解决方案的。空间块是递归生成的,我更愿意使用运算符来使代码更清晰:

diamond 
    = putStr 
    . unlines 
    . fst 
    . (iterate f (["*"], [" "]) !!) 
    where 
    f (d, e) 
     = ( e + d + e 
     ++ d + e + d 
     ++ e + d + e 
      , e + e + e 
     ++ e + e + e 
     ++ e + e + e 
     ) 

    (+) = zipWith (++) 

泛化。 如果我们想有这样的:

   +    
      - -    
      +    
      -  -   
     + + + +   
      -  -   
      +    
      - -    
      +    
    -     -  
    + +    + + 
    -     -  
+  +   +  + 
- - - -   - - - - 
+  +   +  + 
    -     -  
    + +    + + 
    -     -  
      +    
      - -    
      +    
      -  -   
     + + + +   
      -  -   
      +    
      - -    
      +    

那么解决的办法是star 3其中

star 
    = putStr 
    . unlines 
    . (\(d, p, e) -> d) 
    . (iterate f (["-"], ["+"], [" "]) !!) 
    where 
    f (d, p, e) 
     = ( e + p + e 
     ++ d + e + d 
     ++ e + p + e 
      , e + d + e 
     ++ p + e + p 
     ++ e + d + e 
      , e + e + e 
     ++ e + e + e 
     ++ e + e + e 
     ) 

    (+) = zipWith (++) 
+0

我同意,中缀在这里很棒。虽然很多程序员不太喜欢'+'的阴影,但是''''也许会更好一些(默认固定'infixl 9'在这种情况下等于+'infixl 6';并且将它重新声明为'infixr 6'(或'infixr 4',就此而言)实际上会使它更高效一些)。 – leftaroundabout

+0

在'Data.Ratio'中使用'%'。我不介意这样的小例子的阴影。 –