満員御礼の第5回 オフラインリアルタイムどう書く。問題が事前公開されていないために参加してからじゃないとコードを書き始められないと思っている参加者がほとんどかもしれない(自分も含めて:p)けど、事前にやっておくべきことが1つだけある。
それは何かというと、単体テストを書いておくこと。このイベントでは、入出力が文字列で、テストパターンがTSV方式で与えられるということがお約束となっている。なので、TSVだけ差し替えればテストできるようなスケルトンを作っておくとよい。まさかテストなしでいきなりロジックを書き始める、なんて人は居ないよね・・・??
というわけで、色々な言語で作ってみた。
Perl。
use strict; use warnings; use Test::More; use Answer qw(solve); open my $in, '<', 'patterns.tsv' or die; while (<$in>) { tr/\r\n//d; my ($num, $input, $expected) = split /\t/, $_, 3; is +(solve $input), $expected, "TEST $num"; } done_testing; __END__ % prove -v test.t test.t .. ok 1 - TEST \#1 not ok 2 - TEST \#2 ok 3 - TEST \#3 1..3 # Failed test 'TEST \#2' # at test.t line 10. # got: '12345' # expected: '13245' # Looks like you failed 1 test of 3. Dubious, test returned 1 (wstat 256, 0x100) Failed 1/3 subtests Test Summary Report ------------------- test.t (Wstat: 256 Tests: 3 Failed: 1) Failed test: 2 Non-zero exit status: 1 Files=1, Tests=3, 0 wallclock secs ( 0.02 usr 0.01 sys + 0.01 cusr 0.00 csys = 0.04 CPU) Result: FAIL
import unittest from answer import solve class TestSequenceFunctions(unittest.TestCase): def test(self): with open('patterns.tsv') as f: for line in f: num, inputted, expected = line.rstrip().split("\t") self.assertEqual( solve(inputted), expected, "%s failed" % num ) if __name__ == '__main__': unittest.main() # % python2.7 test.py # F # ====================================================================== # FAIL: test (__main__.TestSequenceFunctions) # ---------------------------------------------------------------------- # Traceback (most recent call last): # File "test.py", line 10, in test # solve(inputted), expected, "%s failed" % num # AssertionError: #2 failed # # ---------------------------------------------------------------------- # Ran 1 test in 0.000s # # FAILED (failures=1)
Ruby。
require "runit/testcase" require "runit/cui/testrunner" require "answer.rb" class AnswerTest < RUNIT::TestCase def test IO.foreach("patterns.tsv") do |line| num, input, expected = line.chomp.split("\t") assert_equal (solve input), expected, "#{num} failed." end end end RUNIT::CUI::TestRunner.run(AnswerTest.suite) __END__ % ruby test.rb Loaded suite AnswerTest Started AnswerTest#test: F Finished in 0.00554 seconds. 1) Failure: AnswerTest#test [test.rb:10:in `test' test.rb:8:in `foreach' test.rb:8:in `test' .../ruby/1.8/runit/testcase.rb:42:in `run' .../ruby/1.8/runit/testsuite.rb:23:in `run']: #2 failed. <"12345"> expected but was <"13245">. 1 tests, 2 assertions, 1 failures, 0 errors
Java。
import static org.junit.Assert.*; import java.io.*; public class Test { @org.junit.Test public void simpleAdd() throws Exception { final FileInputStream in = new FileInputStream("patterns.tsv"); final InputStreamReader reader = new InputStreamReader(in, "US-ASCII"); final BufferedReader buffered = new BufferedReader(reader); String line; while ((line = buffered.readLine()) != null) { final String[] parts = line.split("\t"); assertEquals( parts[0] + " failed", Answer.solve(parts[1]), parts[2] ); } } } /* % javac -cp .:junit-4.10.jar Answer.java Test.java; java -cp .:junit-4.10.jar org.junit.runner.JUnitCore Test JUnit version 4.10 .E Time: 0.007 There was 1 failure: 1) simpleAdd(Test) org.junit.ComparisonFailure: #2 failed expected:<1[23]45> but was:<1[32]45> at org.junit.Assert.assertEquals(Assert.java:125) at Test.simpleAdd(Test.java:13) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:24) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at org.junit.runner.JUnitCore.run(JUnitCore.java:136) at org.junit.runner.JUnitCore.run(JUnitCore.java:117) at org.junit.runner.JUnitCore.runMain(JUnitCore.java:98) at org.junit.runner.JUnitCore.runMainAndExit(JUnitCore.java:53) at org.junit.runner.JUnitCore.main(JUnitCore.java:45) FAILURES!!! Tests run: 1, Failures: 1 */
import org.scalatest.FunSuite import scala.io.Source import Answer.solve class Test extends FunSuite { val source = Source.fromFile("patterns.tsv") val lines = source.getLines lines.foreach(line => { val parts = line split '\t' test("TEST " + parts(0)) { assert(solve(parts(1)) === parts(2)) } } ) } /* % scalac -cp .:scalatest_2.9.0-1.8.jar Test.scala Answer.scala; scala -cp .:scalatest_2.9.0-1.8.jar org.scalatest.run Test Run starting. Expected test count is: 3 Test: - TEST #1 - TEST #2 *** FAILED *** "1[23]45" did not equal "1[32]45" (Test.scala:11) - TEST #3 Run completed in 118 milliseconds. Total number of tests run: 3 Suites: completed 1, aborted 0 Tests: succeeded 2, failed 1, ignored 0, pending 0 *** 1 TEST FAILED *** */
module Main where import Test.HUnit import Answer main :: IO () main = do eachLines <- lines `fmap` readFile "patterns.tsv" patterns <- return $ split3 `fmap` eachLines tests <- return $ (TestCase . doAssert) `fmap` patterns runTestTT $ TestList tests return () where split3 str = let (x, xs) = split str (x', x'') = split xs in (x, x', x'') split = (id >< tail) . break (== '\t') doAssert (name, input, expected) = assertEqual name expected (solve input) (><) :: (a -> b) -> (c -> d) -> (a, c) -> (b, d) (f >< g) (x, y) = (f x, g y) -- % runhaskell test.hs -- ### Failure in: 1 -- #2 -- expected: "13245" -- but got: "12345" -- Cases: 3 Tried: 3 Errors: 0 Failures: 1
Perl以外は普段使わない言語なので適当。後、C系統とLISP系統、Groovy、node、PHPなどなどのテストはまだない。pull-req絶賛募集中 :p。
【11/7追記】@emattsan さんがCとC++のスケルトンを実装して下さいました。