10分でコーディングをやってみた

10分でコーディング | プログラミングに自信があるやつこい!!

まずRubyでやってみた。
(予想通り)10分以上かかる。StringとかArrayとかのマニュアルにちゃんと目を通しておかないと、自分の頭に浮かんだ解法とそれをどうRubyで表現できるか(or できないから他の方法を考えるか)が決まらなくてもどかしい。

class Cards
  def self.deal(num_players, deck)
    #players = Array.new(num_players, "")
    players = (0...num_players).map { "" }

    i = 0
    deck.each_char do |card|
      idx = i % num_players
      players[idx] << card
      i += 1
    end

    num = deck.length / num_players
    players.map { |cards| cards[0, num] }
  end
end

describe Cards do
  describe "#deal" do
    it "deals evenly for each player" do
      Cards.deal(3, "123123123").should == ["111", "222", "333"]
    end

    it "deals evenly and discards extra cards" do
      Cards.deal(4, "123123123").should == ["12", "23", "31", "12"]
    end

    it "deals all cards to 1 player" do
      Cards.deal(1, "012345012345012345").should == ["012345012345012345"]
    end

    it "deals no cards when cards are not many enough" do
      Cards.deal(6, "01234").should == ["", "", "", "", "", ""]
    end

    it "deals no cards when there is none" do
      Cards.deal(2, "").should == ["", ""]
    end
  end
end
感想とか
  • 最初からテスト書いてやった。時間が余計にかかるかデバッグが楽になって時間が短縮できるか微妙だけど、テストがあった方が気楽にかける。(そもそも10分で終わらせる気はあんまりなかったとも言う)
  • Array.new(num_players, "")とやると、配列の各要素が同じ文字列オブジェクトを指すことに気づかずはまる。
  • カウンターださいから消したい。
  • Cardsクラス意味ないね。まぁいいか。

そもそも最初に頭に浮かんだ解法は

  1. カード"123123123"を3人に配りたい
  2. 先頭から3つずつに切っていく ["123", "123", "123"]
  3. 各要素の0番目、1番目、3番目を取って出来上がり ["111", "222", "333"]

というもの。

これをGaucheでやってみると

#!/usr/bin/env gosh

(use srfi-1)
(use util.list)
(use gauche.test)

(define (deal num-players deck)
  (let* ((sliced (slices (string->list deck) num-players))
         (cards (filter (lambda (s) (= (length s) num-players)) sliced)))
    (map list->string
         (map (lambda (i) (map (cut ref <> i) cards))
              (iota num-players)))))

(test* "deals evenly (no extra cards)" '("111" "222" "333")
       (deal 3 "123123123"))

(test* "deals evenly (discards extra cards)" '("12" "23" "31" "12")
       (deal 4 "123123123"))

(test* "all cards to 1 player" '("012345012345012345")
       (deal 1 "012345012345012345"))

(test* "no cards to all players" '("" "" "" "" "" "")
       (deal 6 "01234"))

(test* "empty deck" '("" "")
       (deal 2 ""))
感想とか
  • slices 便利
  • map 多すぎてやっぱり微妙かもしれない

追記

よく考えたらzipが使える。そのかわりif文が増えるのであまり短くはならないけど。

(define (deal num-players deck)
  (if (< (string-length deck) num-players)
    (make-list num-players "")
    (let* ((sliced (slices (string->list deck) num-players))
           (cards (filter (lambda (s) (= (length s) num-players)) sliced)))
      (map list->string (apply zip cards)))))

同じことをHaskell

import Data.List (transpose)

deal :: Int -> String -> [String]
deal numPlayers deck
    | numPlayers > length deck = replicate numPlayers ""
    | otherwise = transpose $ slices' numPlayers deck

slices :: Int -> [a] -> [[a]]
slices n [] = []
slices n xs = case splitAt n xs of
                (y,ys) -> y : slices n ys

slices' :: Int -> [a] -> [[a]]
slices' n xs = filter (\x -> n == length x) $ slices n xs
    
main = mapM_ (\ (n:deck:[]) -> print $ deal (read n) deck)
       [ ["3", "123123123"]          -- ["111","222","333"]
       , ["4", "123123123"]          -- ["12","23","31","12"]
       , ["1", "012345012345012345"] -- ["012345012345012345"]
       , ["6", "01234"]              -- ["", "", "", "", "", ""]
       , ["2", ""]                   -- ["", ""]
       ]
感想とか
  • 結局やりたかった事は transpose (+ slices) だった。ここで行列の転置が出てくるとは。
  • (Gaucheの) slices 相当は標準にはなさそう。自分で書いたけどあやしい。
  • テストの書き方よく知らないのでパス。


もっとRuby勉強してください自分。(Scheme, Haskell は十分だという意味ではもちろんない)

RailsでRESTfulにposts/:year/:month/:dayするにはどうしたらいいんだろう (続き)

前の記事の続き。

id:tkawa さんがコメントで教えてくれてとっくに解決していましたが、

http://api.rubyonrails.org/classes/ActionDispatch/Routing.html
の「Pretty URLs」によると、
match '/articles/:year/:month/:day' => 'articles#find_by_id', :constraints => { ... }
のように書けばいいようです。
routes.rbに「:action」を書いてしまうのがレガシーでRESTfulではないということで、「posts/:year/:month/:day」はRESTfulと言っていいと思いますよ。


この前、Rails3 cheat sheets http://blog.envylabs.com/2010/12/rails-3-cheat-sheets/ にも同じ方法が載っているのを見つけたので改めてまとめておきます。

# これは有効なrouting

# OPTIONAL PARAMETERS
match '/posts(/:yy(/:mm))' => "posts#index"

class PostsController < ApplicationController
  def index
    # params[:yy]
    # params[:mm]
  end
end

# CONSTRAINTS
match '/:year' => "posts#index",
  :constraints => {:year => /\d{4}/, :ip => /192\.168\.1\.\d{1,3}/}

結局、下のコードがLegacyとされているのは、全てのコントローラーがアクセスできるようになってしまう問題があるためで、「RESTfulではないから」と思ったのはただの読み違いだったということでした。

# こっちはLegacyなrouting

# LEGACY ROUTE
match '/:controller(:/:action(/:id(.:format)))'