CLOVER🍀

That was when it all began.

Re: 10分でコーディング

No Programming, No Lifeで出ていたので、ちょっとやってみました。

元ネタ。

難易度 激簡単 目標時間10分以内

あなたはこれからトランプを配っていきます。

あなたにはトランプを配る人数、
そしてトランプが渡されます。

今回はとても簡単なので例題で説明します。

例)
2つの引数がもらえます。

3
"123123123"

最初の3はプレイヤーの人数を示しています。
"123123123" はトランプの並びを示しています。あなたはこのなかのトランプを
配っていかなければなりません。

この場合、あなたのプログラムは

{"111","222","333"}

を返さなければなりません。

"111"は一番めのプレイヤーが受け取るトランプです。
"222"が2番目のプレイヤーが受け取るトランプです。
"333"が2番目のプレイヤーが受け取るトランプです。

ところが、以下のような場合もあります。

すべてのプレイヤーは同じ数だけのトランプを受け取らなければなりません。
ですので

4
"123123123"

この場合、あなたのプログラムは

{"12","23","31","12"}

を返さなければなりません。

{"123","23","31","12"} は駄目です。


では、以下にもうすこし例をのせます。

例1)
6
"012345012345012345"
Returns: {"000", "111", "222", "333", "444", "555" }

例2)
4
"111122223333"
Returns: {"123", "123", "123", "123" }

例3)
1
"012345012345012345"
Returns: {"012345012345012345" }

例4)
6
"01234"
Returns: {"", "", "", "", "", "" }

例5)
2
""
Returns: {"", "" }


クラス名、などは以下のとおりです。

Class:    Cards
Method:    deal
Parameters:  int, String
Returns:   String
Method signature: String
deal(int numPlayers, String deck)

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

Scalaで解いてみました。

Cards.scala

import scala.annotation.tailrec
import scala.collection.Iterator

class Cards {
  def deal(numPlayers: Int, deck: String): Array[String] = {
    val join: ((String, Char)) => String = { case (x, y) => x + y }

    @tailrec
    def dealInner(acc: List[String], xs: List[Char]): List[String] =
      xs splitAt numPlayers match {
        case (first, rest) if first.size == numPlayers =>
          dealInner((acc zip first).map(join), rest)
        case _ => acc
      }

    dealInner(
      Iterator.continually("").take(numPlayers).toList,
      deck.toList
    ).toArray
  }
}

object Cards extends App {
  val cards = new Cards
  p(cards.deal(6, "012345012345012345"))
  p(cards.deal(4, "111122223333"))
  p(cards.deal(1, "012345012345012345"))
  p(cards.deal(6, "01234"))
  p(cards.deal(2, ""))

  def p(array: Array[String]): Unit =
    println {
      array map { s => '"' + s + '"' } mkString("{", ", ", "}")
    }
}

実行結果。

{"000", "111", "222", "333", "444", "555"}
{"123", "123", "123", "123"}
{"012345012345012345"}
{"", "", "", "", "", ""}
{"", ""}

でも、余裕で10分越えたよ(笑)。だいたい、15分くらいかかったような…。

今日の問題はかなり簡単です。

できるだけ早い時間でエレガントなコードを書きましょう。
あまりに簡単なので制限時間を10分としてやってみてください。

これ以上かかった人は

自分はかなりプログラミングができない。

とつらい事実を認識しましょう。

そして、これからすごくなりましょう。

さて、頑張りますか…。

(追記)
こういう機会に、普段機会がないと使わない言語を使ってみるってことで、Python版を作成。

#!/usr/bin/python
# -*- coding: utf-8 -*-

def deal(numPlayers, deck):
    def dealInner(acc, current):
        first = current[0:numPlayers]
        rest = current[numPlayers:]

        if len(first) == numPlayers:
            return dealInner(map(lambda x, y: x + y,  acc, first), rest)
        else:
            return acc

    init = [''] * numPlayers

    return dealInner(init, deck)

if __name__ == '__main__':
    print deal(6, '012345012345012345')
    print deal(4, '111122223333')
    print deal(1, '012345012345012345')
    print deal(6, '01234')
    print deal(2, '')

考え方は、Scala版と全く同じ。
関数とか思いっきり忘れてて、30分くらいかかりましたが…。

(さらに追記)
いろいろあって、Ruby版も書きました。
cards.rb

#!/usr/bin/ruby
# -*- coding: utf-8 -*-

class Cards
  def deal(numPlayers, deck)
    cards = Array.new(numPlayers, "")
    if numPlayers > deck.length
      cards
    else
      limit = deck.length - deck.length % numPlayers
      zipped = (0...limit).to_a.zip(deck.split(//).slice(0, limit))
      zipped.reduce(cards) { |acc, elm|
        acc[elm[0] % numPlayers] += elm[1]
        acc
      }
    end
  end
end


cards = Cards.new
p cards.deal(6, "012345012345012345")
p cards.deal(4, "111122223333")
p cards.deal(1, "012345012345012345")
p cards.deal(6, "01234")
p cards.deal(2, "")
p cards.deal(3, "123123123")
p cards.deal(4, "123123123")

何気に、初Ruby。Rubyっぽくないなどあったら、ご容赦いただけますよう、お願い致します…。

(追記)
畳み込みで、ということで、何も考えずに書き換えてみる。

  def deal(numPlayers: Int, deck: String): Array[String] = {
    val join: ((String, Char)) => String = { case (x, y) => x + y }

    (0 until (deck.size / numPlayers))
      .foldLeft(
      (Iterator.continually("").take(numPlayers).toList,
       deck.toList)) {
      case ((acc, xs), _) => xs splitAt numPlayers match {
        case (first, rest) if first.size == numPlayers =>
          ((acc zip first).map(join), rest)
        case _ => (acc, xs)
      }
    }._1.toArray
  }

…ちょっと、書き直したことを後悔しました。

sliceにしとく?

  def deal(numPlayers: Int, deck: String): Array[String] =
    (0 until (deck.size / numPlayers))
      .foldLeft(Iterator.continually("").take(numPlayers).toList) { (acc, i) =>
      deck.slice(i * numPlayers, (i * numPlayers) + numPlayers) match {
        case cs if cs.size == numPlayers => (acc zip cs) map { case (x, y) => x + y }
        case cs => acc
      }
    }.toArray

なんか元々の課題を難しくしてる気がしたので、だったらImmutableにこだわらずに素直にArray使った方がわかりやすいかなー、と。

  def deal(numPlayers: Int, deck: String): Array[String] =
    ((0 until deck.size) zip deck)
      .take(deck.size - (deck.size % numPlayers))
      .foldLeft(Array.fill(numPlayers)("")) {
      case (cards, (i, c)) =>
        cards.updated(i % numPlayers, cards(i % numPlayers) + c)
    }

まあ、Ruby版とやってることはだいたい一緒ですね。