2010-05-12 61 views
10

我正在研究一个Rails模板,并试图编写一些代码,允许我在多个列中填充一个或多个ul标签“从上到下”和“从左到右”的列我指定。我刚刚得到了Ruby的诀窍,所以我无法弄清楚这一点。我也很好奇这个有用的片段的惯用Haskell版本。改进Clojure的版本加赞赏:你会如何在Ruby和/或Haskell中编写这个Clojure代码片段?

(defn table [xs & {:keys [cols direction] 
        :or {cols 1 direction 'right}}] 
    (into [] 
     (condp = direction 
      'down (let [c (count xs) 
         q (int (/ c cols)) 
         n (if (> (mod c q) 0) (inc q) q)] 
        (apply map vector (partition n n (repeat nil) xs))) 
      'right (map vec (partition cols cols (repeat nil) xs))))) 

随着该位的代码,然后我可以做到以下几点:

(table (range 10) :cols 3) 

打印出来,这会看起来像这样:

0 1 2 
3 4 5 
6 7 8 
9 

而棘手一个:

(table (range 10) :cols 3 :direction 'down) 

看起来像这样:

0 4 8  
1 5 9  
2 6   
3 7   
+0

三样东西,你也许可以改变Clojure的版本 1 。使用defnk(clojure.contrib.def)它有点好看。 2.关键字而不是符号 3.而不是做转换为向量两种方式统一它与(vec(地图vec(condp .....))) – nickik 2011-04-19 14:34:59

回答

4

我可能会写在Haskell这样的事情,使用Data.List.Split包从Hackage:

import Data.List  (intercalate, transpose) 
import Data.List.Split (splitEvery) 

data Direction = Horizontal | Vertical deriving (Eq, Read, Show) 

table :: Direction -> Int -> [a] -> [[a]] 
table Horizontal cols xs = splitEvery cols xs 
table Vertical cols xs = let (q,r) = length xs `divMod` cols 
           q' = if r == 0 then q else q+1 
          in transpose $ table Horizontal q' xs 

showTable :: Show a => [[a]] -> String 
showTable = intercalate "\n" . map (intercalate "\t" . map show) 

main :: IO() 
main = mapM_ putStrLn [ showTable $ table Horizontal 3 [0..9] 
         , "---" 
         , showTable $ table Vertical 3 [0..9] ] 

一些这方面,像Direction类型和transpose伎俩,从jkramer的答案的。在Haskell中,我不会使用关键字参数来实现这样的事情(它并不是真的有这样的东西,但是可以用Edward Kmett的答案中的记录来模拟它们),但是我首先提出这些参数是因为它对于部分应用程序更有用(defaultTable = table Horizontal 1)。 splitEvery函数只是将一个列表分成合适大小的列表;其余的代码应该很简单。 table函数返回列表的列表;要获取一个字符串,showTable函数将插入制表符和换行符。 (intercalate函数连接列表的列表,将它们与给定列表分开。它类似于学习Perl/Python的/ Ruby的join,只对名单,而不是只是字符串)

2

这是我在Haskell中快速入侵的东西。我敢肯定,这车,可以进行优化,但它的东西入手:

import System.IO 
import Data.List 

data Direction = Horizontal | Vertical 

main = do 
    putStrLn $ table [1..9] 3 Horizontal 
    putStrLn "---" 
    putStrLn $ table [1..9] 3 Vertical 


table xs ncol direction = 
    case direction of 
     Horizontal -> format (rows strings ncol) 
     Vertical -> format (columns strings ncol) 
    where 
     format = intercalate "\n" . map (intercalate " ") 

     strings = map show xs 

     rows xs ncol = 
      if length xs > ncol 
       then take ncol xs : rows (drop ncol xs) ncol 
       else [xs] 

     columns xs = transpose . rows xs 

输出:

1 2 3 
4 5 6 
7 8 9 
--- 
1 4 7 
2 5 8 
3 6 9 
+0

这不会给非正方形的正确答案尽管如此,垂直列的数量;而不是用'ncol'打印'_',而是用'_'打印'ncol'的东西('_'表示“需要任何数字”)。一般来说,我认为可以收紧;看到我的答案。 – 2010-05-12 17:59:16

+0

啊,我正在寻找像splitEvery这样的东西,但没有找到它。 – jkramer 2010-05-12 18:59:35

4

我不能读Clojure的代码(我从来没有使用的语言),但基于这些例子,下面是我如何在Ruby中完成它。

def table array, cols, direction 
    if direction==:down 
     if array.size%cols != 0 
     array[(array.size/cols+1)*cols-1]=nil 
     #putting nil in the last space in the array 
     #also fills all of the spaces before it 
     end 
     newarray=array.each_slice(array.size/cols).to_a 
     table newarray.transpose.flatten(1), cols, :across 
    elsif direction==:across 
     array.each_slice(cols) do |row| 
     puts row.join(" ") 
     end 
    else 
     raise ArgumentError 
    end 
end 
+0

不错的解决方案。在Ruby和Haskell中看到一个实际问题的变体很酷。 – dnolen 2010-05-13 14:39:40

2

我的红宝石解决方案

def table(values) 
    elements = values[:elements] 
    cols = values[:cols] 
    rows = (elements.count/cols.to_f).ceil 

    erg = [] 

    rows.times do |i| 
    cols.times do |j| 
     erg << elements[values[:direction] == 'down' ? i+(rows*j) : j+i*(rows-1)] 
     if erg.length == cols 
     yield erg 
     erg = [] 
     end   
    end 
    end 
    yield erg 
end 

用法和输出:

table(:elements => [0,1,2,3,4,5,6,7,8,9], :cols => 3) do |h,i,j| 
    puts h.to_s << " " << i.to_s << " " << j.to_s 
end 

puts "---" 

table(:elements => [0,1,2,3,4,5,6,7,8,9], :cols => 3, :direction => "down") do |h,i,j| 
    puts h.to_s << " " << i.to_s << " " << j.to_s 
end 

0 1 2 
3 4 5 
6 7 8 
9 
--- 
0 4 8 
1 5 9 
2 6 
3 7 
1
import Data.Array 

stride :: Int -> Int -> Int 
stride count cols = ceiling (fromIntegral count/fromIntegral cols) 

type Direction = Int -> Int -> Int -> Int -> Int 

right :: Direction 
right count cols x y = y * cols + x 

down :: Direction 
down count cols x y = x * stride count cols + y 

data Options = Options { cols :: Int, direction :: Direction } 

options :: Options 
options = Options 1 right 

table :: Options -> [a] -> Array (Int,Int) (Maybe a) 
table (Options cols dir) xs 
    = listArray newRange (map f (range newRange)) 
    where count = length xs 
      rows = stride count cols 
      newRange = ((0,0),(rows-1,cols-1)) 
      f (y, x) 
       | ix < count = Just (xs !! ix) 
       | otherwise = Nothing 
       where ix = dir count cols x y 

这给了我们原始查询的相当地道的近似完整的可选参数:

*Main> table options { cols = 3 } [1..10] 
listArray ((0,0),(3,2)) [Just 1, Just 2, Just 3 
         ,Just 4, Just 5, Just 6 
         ,Just 7, Just 8, Just 9 
         ,Just 10,Nothing,Nothing] 

*Main> table options { direction = down, cols = 3 } [1..10] 
listArray ((0,0),(3,2)) [Just 1,Just 5,Just 9 
         ,Just 2,Just 6,Just 10 
         ,Just 3,Just 7,Nothing 
         ,Just 4,Just 8,Nothing] 

我以数组形式离开了中间结果,因为您已经表明他们计划将它们格式化为表格或ul标签。

2

切片和压缩和解给出了一个简单的Ruby解决方案:

def table(range, cols, direction=:right) 
    if direction == :right 
    range.each_slice cols 
    else 
    columns = range.each_slice((range.to_a.length - 1)/cols + 1).to_a 
    columns[0].zip *columns[1..-1] 
    end 
end 


puts table(0..9, 3, :down).map { |line| line.join ' ' }