2010-05-12 64 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 ' ' } 
相關問題