2010-05-12 26 views
10

Tôi đang làm việc trên mẫu Rails và đang cố gắng viết một đoạn mã cho phép tôi điền một bảng hoặc nhiều cột thẻ ul "từ trên xuống dưới" và "từ trái sang phải" qua nhiều cột Tôi chỉ định. Tôi chỉ nhận được hang của Ruby vì vậy tôi không thể hình dung ra điều này. Tôi cũng tò mò về một phiên bản Haskell thành ngữ cho đoạn trích hữu ích này. Cải tiến lên phiên bản Clojure đánh giá cao:Bạn sẽ viết đoạn mã Clojure này trong Ruby và/hoặc Haskell như thế nào?

(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))))) 

Với chút mã này sau đó tôi có thể làm như sau:

(table (range 10) :cols 3) 

in ra này sẽ trông giống như vậy:

0 1 2 
3 4 5 
6 7 8 
9 

Và phức tạp hơn một:

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

Có vẻ như vậy:

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

Ba điều bạn có thể có thể thay đổi trên phiên bản clojure 1 Sử dụng defnk (clojure.contrib.def) của nó một chút tốt đẹp để đọc. 2. từ khóa thay vì ký hiệu 3. thay vì thực hiện chuyển đổi thành vectơ hai cách hợp nhất nó với (vec (bản đồ vec (condp .....))) – nickik

Trả lời

4

tôi có lẽ sẽ viết một cái gì đó như thế này trong Haskell, bằng cách sử dụng gói Data.List.Split từ 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] ] 

Một số này, giống như kiểu Directiontranspose lừa, được bắt nguồn từ câu trả lời của jkramer. Tôi sẽ không sử dụng các đối số từ khóa cho một cái gì đó như thế này trong Haskell (nó không thực sự có những thứ như vậy, nhưng bạn có thể mô phỏng chúng bằng cách sử dụng các bản ghi như trong câu trả lời của Edward Kmett), nhưng tôi đặt các đối số đó trước vì nó hữu ích hơn với ứng dụng một phần (defaultTable = table Horizontal 1). Chức năng splitEvery chỉ sắp xếp một danh sách thành các danh sách có kích thước phù hợp; phần còn lại của mã phải đơn giản. Hàm table trả về một danh sách các danh sách; để nhận chuỗi, hàm showTable chèn các tab và dòng mới. (Chức năng intercalate nối một danh sách các danh sách, tách chúng với danh sách đã cho.Nó tương tự như Perl/Python/Ruby join, chỉ cho các danh sách thay vì chỉ là chuỗi)

2

Đây là điều tôi đã nhanh chóng bị hack trong Haskell. Tôi chắc chắn đó là lỗi và có thể được tối ưu hóa, nhưng nó là cái gì để bắt đầu với:

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 

Output:

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

Điều này không cung cấp câu trả lời đúng cho không vuông số lượng các cột sẽ theo chiều dọc, mặc dù; thay vì in một cái gì đó là '_' bởi' ncol', nó in một cái gì đó là 'ncol' bởi' _' ('_' chỉ ra" số bất kỳ là cần thiết "). Nói chung, tôi nghĩ rằng nó có thể được thắt chặt; xem câu trả lời của tôi –

+0

Ahh, tôi đang tìm kiếm thứ gì đó như splitEvery, nhưng không tìm thấy nó. – jkramer

4

tôi không thể đọc mã clojure (Tôi chưa bao giờ sử dụng ngôn ngữ), nhưng dựa trên các ví dụ, đây là cách tôi sẽ làm điều đó trong 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

Giải pháp tốt. Thật tuyệt khi thấy các biến thể về một vấn đề thực tế trong Ruby và Haskell. – dnolen

2

giải pháp ruby ​​My

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 

Cách sử dụng và sản lượng:

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 

này cho chúng ta một xấp xỉ khá thành ngữ truy vấn ban đầu của bạn hoàn chỉnh với đối số tùy chọn:

*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] 

Tôi đã để lại kết quả trung gian dưới dạng mảng như bạn đã chỉ ra rằng bạn đã lên kế hoạch định dạng chúng dưới dạng bảng hoặc thẻ ul.

2

Cắt lát và nén đưa ra một giải pháp của Ruby đơn giản:.

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 ' ' } 
Các vấn đề liên quan