您将如何用 Ruby 和/或 Haskell 编写这个 Clojure 片段?

发布于 2024-09-01 23:59:50 字数 945 浏览 5 评论 0原文

我正在开发一个 Rails 模板,并试图编写一些代码,允许我在我指定的任意多列中填充一个表或多列 ul 标签“从上到下”和“从左到右” 。我刚刚掌握 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        

I was working on a Rails template and was trying to write a bit of code that allows me to populate a table or multiple columns of ul tags "top-to-bottom" and "left-to-right" across however many columns I specify. I'm just getting the hang of Ruby so I couldn't figure this out. I'm also curious about an idiomatic Haskell version for this useful snippet. Improvements to Clojure version appreciated:

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

With this bit of code I can then do the following:

(table (range 10) :cols 3)

Printed out this would look like so:

0    1    2 
3    4    5 
6    7    8
9

And the trickier one:

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

Looks like so:

0    4    8    
1    5    9    
2    6        
3    7        

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

神经暖 2024-09-08 23:59:50

我可能会使用 Hackage 中的 Data.List.Split 包在 Haskell 中编写类似的内容:

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< /code> 技巧,源自 jkramer 的答案。我不会在 Haskell 中使用关键字参数来实现类似的东西(它实际上没有这样的东西,但你可以使用 Edward Kmett 的答案中的记录来模拟它们),但我把这些参数放在第一位,因为它对于部分应用程序更有用(defaultTable = 表水平 1)。 splitEvery 函数只是将列表分成适当大小的列表;其余的代码应该很简单。 table 函数返回列表的列表;为了获取字符串,showTable 函数会插入制表符和换行符。 (intercalate 函数连接一个列表列表,将它们与给定列表分开。它类似于 Perl/Python/Ruby 的 join,仅适用于列表而不仅仅是字符串。)

I would probably write something like this in Haskell, using the Data.List.Split package from 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] ]

Some of this, like the Direction type and the transpose trick, was derived from jkramer's answer. I wouldn't use keyword arguments for something like this in Haskell (it doesn't really have such things, but you can emulate them using records as in Edward Kmett's answer), but I put those arguments first because it's more useful with partial application (defaultTable = table Horizontal 1). The splitEvery function just chunks a list into lists of the appropriate size; the rest of the code should be straightforward. The table function returns a list of lists; to get a string, the showTable function inserts tabs and newlines. (The intercalate function concatenates a list of lists, separating them with the given list. It's analogous to Perl/Python/Ruby's join, only for lists instead of just strings.)

羅雙樹 2024-09-08 23:59:50

我无法阅读 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

I can't read the clojure code (I've never used the language), but based on the examples, here's how I'd do it in 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
蓦然回首 2024-09-08 23:59:50

这是我在 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

Here is something I quickly hacked in Haskell. I'm sure it's buggy and can be optimized, but it's something to start with:

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
违心° 2024-09-08 23:59:50

我的红宝石解决方案

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 

My ruby solution

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

Usage and output:

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 
涫野音 2024-09-08 23:59:50

切片和压缩提供了一个简单的 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 ' ' }

Slicing and zipping gives a straightforward Ruby solution:

 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 ' ' }
沫雨熙 2024-09-08 23:59:50
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 标记。

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

This gives us a fairly idiomatic approximation of your original query complete with optional arguments:

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

I left the intermediate results in array form as you had indicated that you them planned to format them as a table or ul tags.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文