【问题标题】:How would you write this Clojure snippet in Ruby and/or Haskell?你会如何用 Ruby 和/或 Haskell 编写这个 Clojure 代码片段?
【发布时间】:2011-02-18 15:57:13
【问题描述】:

我正在编写一个 Rails 模板,并试图编写一些代码,让我可以填充一个表格或多列 ul 标记“从上到下”和“从左到右”跨越许多我指定的列。我刚刚掌握了Ruby的窍门,所以我无法弄清楚。我也很好奇这个有用的 sn-p 的惯用 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        

【问题讨论】:

  • 您可以在 clojure 版本 1 上更改三件事。使用 defnk (clojure.contrib.def) 读起来有点好。 2. 关键字而不是符号 3. 不是将其转换为向量两种方式将其与 (vec (map vec (condp .....))) 统一起来

标签: ruby haskell clojure language-comparisons


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 不错的解决方案。在 Ruby 和 Haskell 中看到一个实际问题的变化很酷。
    【解决方案3】:

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

    【讨论】:

    • 这并不能给出垂直方向的非方形列数的正确答案;它不是通过ncol 打印_ 的内容,而是通过_ 打印ncol 的内容(_ 表示“任何数字都是必要的”)。一般来说,我认为可以收紧;看我的回答。
    • 啊,我一直在寻找类似 splitEvery 的东西,但没有找到。
    【解决方案4】:

    我的红宝石解决方案

    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 
    

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:
        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 标记。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-05-11
          • 1970-01-01
          • 2020-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多