Hatena::ブログ(Diary)

※ただし体調がよければ

世の中分からないことだらけ。ツッコミ大歓迎です。

2010年07月11日

「ほおずき」?「ほうづき」?

昨日は、某イベントで浅草のほおずき市に行ってきました。

面白かったのは、「ほおずき」の仮名遣いがいろいろあったこと。「ほうづき」だったり「ほおづき」だったり・・・。そこで、ちょっとこのことについて調べてみました。

Wikipediaによると、ほおずきの語源は「頬を突く」らしいです。だから、それにしたがえば「ほおづき」がもっとも適切な仮名遣いということになりますね。

ところがところが。

現代の日本語では、「ほおずき」を本則とするという(暗黙の?)合意があるようです。Googleで検索した結果も以下の通り。

「ほおずき」の方が「ほおづき」よりも約200倍多くヒットします。

また、ATOK 2008で「ほうずき」を変換すると「ほおずきの誤り」と指摘され、「ほおづき」の場合は「ほおずきが本則」と指摘されます。

さらに、ほおずきの植物としての学名は「ホオズキ」です。

これらの理由から、特別の事情がない限り「ほおずき」と書くのが望ましいといえるでしょう。

もちろん、それなりに使用されている以上、「ほおずき」以外の表記が誤りとは言えませんが(ATOKはそう言うけど)、言葉をコミュニケーションのプロトコルとして考える以上、特別な意味を持たせくない場面ではもっとも一般的な仮名遣いを使用すべきでしょう。

ちなみに、ほおずきの歴史的仮名遣いは「ほほづき」です。なるほど、語源に正確ですね。

そういえば

検索結果でこれほど「ほおずき」が圧倒的にヒットする背景には、前述したATOKの訂正機能が影響しているのかもしれません。もしそうであれば、これはIMEが日本語を(自然な状態から)変えた一つの例と言えるでしょう。

IMEにそのような機能がない状態ではどのような分布になるのか。興味深いところです。

2010年03月29日

食糧自給率について考えをまとめてみた

Twitterで食糧自給率についての議論が行われています。

食糧自給率については、僕も何度か考えたことがあったので、今回の議論で新たに学んだ内容も含めて一度簡単にまとめてみます。

Q. 安全保障のために自給率を引き上げる必要はあるか?

ない。

自給率を向上さなくても、輸入先を分散させることでリスクは分散できる。

また、石油も肥料も農薬も輸入に頼っている現状では、いくら自給率が高くてもそれらも輸入できなくなれば農産物を育て流通させることは困難になる。

したがって、安全保障のための自給率向上には、大きな効果はない。

Q. 日本の農業は衰退させるのが正しい選択か?

違う。

世界の人口は増え続けており、必要な食糧も増え続けている。食糧の増産は必要不可欠だが、そのときに増産のネックとなるのは、労働力でもなく土地でもなく水資源である。

現在一部の地域では、川や井戸の水を持続可能な限界を超えて利用する農業が行われており、その地域ではいずれ現在と同じ規模での農業は行えなくなることが推測される。増産が求められる中で、生産量が減少する可能性があるのだ。

その点、日本は水資源が豊富で、何百年にもわたって持続可能な農業を行ってきた実績がある。日本はその強みを生かし、自給率にはこだわらずに農作物の輸出国となるべく努力をすべきだ。

しかし、現状日本の農作物には価格競争力がない。価格競争力がないのは、小さな耕地面積しか持たない兼業農家が多く、大型機械を用いた効率的な農業が実現できていないからである。したがって、非効率的な生産を行っている小規模な兼業農家をなくし、大型資本が農業ビジネスに参入しやすいように法律を整備する必要がある。


根拠となるデータを探す余裕がないため、適当に書いてます。

ツッコミお願いします。

2010年03月04日

反党行為に厳しいのは当然

時事ドットコムにおかしなブクマコメントがたくさん付いている。

記事の内容は以下の通り。

鳩山由紀夫首相は3日午後の参院予算委員会で、民主党の運営をめぐり、犯罪よりも反党行為により重い処分を下すのは当然との認識を示した。改革クラブ大江康弘氏の質問に答えた。

 大江氏は、2008年に反党行為で自らが民主党から除籍処分となったことに触れた上で、政治資金規正法違反罪で逮捕・起訴された民主党石川知裕衆院議員が同党から「何も問われていない」と首相にただした。これに対し、首相は「党にとっては反党行為が一番厳しく罰せられなくてはならない」と答弁した。 (2010/03/03-19:23)

http://www.jiji.com/jc/c?g=pol_30&k=2010030300824

これを読んで、至極真っ当で正しい判断だと感じたのだが、ブクマコメントでは批判の嵐。僕にはその理由が理解できない。

鳩山さんがいう反党行為が具体的にどのようなものかというのは分からない。しかし一般的に言って、社会ではその社会を崩壊させるような行為がもっとも厳しく罰せられるものだ。

たとえば、日本の刑法では、クーデターの場合は

第七十七条

国の統治機構を破壊し、又はその領土において国権を排除して権力を行使し、その他憲法の定める統治の基本秩序を壊乱することを目的として暴動をした者は、内乱の罪とし、次の区別に従って処断する。

一  首謀者は、死刑又は無期禁錮に処する。

スパイの場合は

外患誘致

第八十一条

外国と通謀して日本国に対し武力を行使させた者は、死刑に処する。

(外患援助)

第八十二条

日本国に対して外国から武力の行使があったときに、これに加担して、その軍務に服し、その他これに軍事上の利益を与えた者は、死刑又は無期若しくは二年以上の懲役に処する。

と、実際に人が死んだかどうかにかかわらず、日本社会を根底から覆すような反社会的行為はいずれも死刑になることが書かれている。普通の殺人よりも重い。

だから、民主党という社会の中でも、民主党の存続を危うくさせるような行為、つまり反党行為が一番厳しく罰せられるのは当然。

粛清が云々とかなんなの?

ブクマコメントの中で特にひどいものに「そのうち粛清とかいって銃殺とかするつもりなんじゃ」というものがあったが、反党行為としてたとえば除名にすると粛清(殺害)するのとでは全く意味が違う。

除名というのは民主党の党則で定められたもので、民主党という社会の枠組みの中で明文化されているものだ。一方、粛清は一般にそのような規則が定められているものでもないし、なにより党という社会が本来影響力を持つべき範囲を逸脱している。入党するということは、党に命を預けるということではない。


どの社会においても、一度その社会の一員になったらその規則にしたがうのが大原則だ。現在の民主党に不満がある党員がいるのなら、同じ考えを持つ仲間を見つけて内乱を起こし、除名というもっとも重い処罰を受けたら新党を作ればいい。

2010年02月26日

Google DevFest 2010のクイズ(2)

Google DevFest 2010の参加者選別のために出題されたクイズ(その2)。

パッチワーク

ここに "A" または "B" という文字のみを含む 600 桁、600 行のテキストがあります。これを 600 x 600 の升目状に並べ、上下左右に同じ文字がある部分をつながっているとみなします。

まず、最も多くの文字がつながっている領域をすべて "_" で塗りつぶしてください。 最も多くの文字がつながっている領域が複数存在するならば、それらすべての領域を "_"で塗りつぶすこととします。

そして、各行ごとに "_" が何個含まれているかを数え、それらの数値を改行区切りのテキスト(600 行)として答えてください。

以下の例1を見てください。この入力には単一の文字4つでつながった領域が3箇所あります。これらすべてが「最も多くの文字がつながっている領域」なので、全て"_"で塗りつぶし、その数を数えています。

http://devquiz.appspot.com/problems?problem=patchwork

具体的には、

ABAAB
BABAA
BAABB
ABABB
BABAA

という入力に対して

AB__B
B_B__
B____
AB___
BABAA

という塗りつぶしを行い、

2
3
4
3
0

という出力を行う。

僕はScala (Scala 2.8)で書いた。これが初めてのScalaプログラム。多分、初めてにしてはそこそこよく書けてる(自画自賛)

package googlepatchwork

import scala.io.Source
import scala.collection.mutable.Set

object Main {

  def main(args: Array[String]) {
    measure {
      val patchwork = toPatchwork(Source.fromPath("test.txt").getLines().toSeq)

      fillAreas(patchwork, areaToFill(patchwork))

      // 塗りつぶした部分を出力
      println(patchwork)

      val nums = countFilledCells(patchwork)
      
      // 最終的な答えを出力
      println(nums.mkString("\n"))
    }
  }
  
  def measure(toRun: => Unit) {
    val time = System.currentTimeMillis
    try {
      toRun
    } finally {
      val diff = System.currentTimeMillis - time
      System.err.println("Executed in " + diff + "ms.")
    }
  }
  
  def countFilledCells(pw: Patchwork[Char]): Array[Int] = {
    val res = Array.fill[Int](pw.height)(0)
    for ((x, y) <- pw.indices) {
      if (pw(x, y) == '_') {
        res(y) += 1
      }
    }
    res
  }

  def toPatchwork(str: Seq[String]): Patchwork[Char] = {
    val lines = str.map(_.trim).filterNot(_.isEmpty)
    new Patchwork[Char](
      lines(0).length, lines.size, 'C', lines.mkString.toCharArray)
  }
  
  def fillAreas(pw: Patchwork[Char], toFill: Set[(Int, Int)]) {
    toFill.foreach {
      case (x: Int, y: Int) => pw(x, y) = '_'
    }
  }
  
  def areaToFill(pw: Patchwork[Char]): Set[(Int, Int)] = {
    val scanned = new Patchwork[Boolean](
      pw.width, pw.height, true, Array.fill[Boolean](pw.width * pw.height)(false))
    var max = 0
    var cellsToFill = Set[(Int, Int)]()
    for ((x, y) <- pw.indices) {
      if (!scanned(x, y)) {
        val area = getNeighbors(pw, x, y)
        area.foreach {
          case (x: Int, y: Int) => scanned(x, y) = true
        }
        
        if (max < area.size) {
          max = area.size
          cellsToFill = area
        } else if (max == area.size) {
          cellsToFill ++= area
        }
      }
    }
    cellsToFill
  }
  
  def getNeighbors(pw: Patchwork[Char], x: Int, y: Int): Set[(Int, Int)] = {
	  val value = pw(x, y)
	  def neighbors(scanned: Set[(Int, Int)], curX: Int, curY: Int): Set[(Int, Int)] = {
	 	  if (pw(curX, curY) == value && !scanned.contains((curX, curY))) {
	 		  val nextCand = List((curX + 1, curY), (curX - 1, curY), (curX, curY + 1), (curX, curY - 1))
	 	 	  ((scanned + ((curX, curY))) /: nextCand) ((scanned2, next) => neighbors(scanned2, next._1, next._2))
	 	  } else
	 	 	  scanned
	  }
	  neighbors(Set(), x, y)
  }
}

class Patchwork[T](
  val width: Int, val height: Int, val defaultValue: T, private val values: Array[T]) {

  require(width * height == values.size)
  
  def indices = new Iterable[(Int, Int)] {
	  
	  private val len = width * height
	  
	  override def iterator = new Iterator[(Int, Int)] {
	 	  
	 	private var idx = 0
	 	
	 	def next = if (idx < len) {
	 	  val ret = (idx % width, idx / height)
	 	  idx += 1
	 	  ret
	 	} else {
	 	  throw new IllegalStateException
	 	}
	 	
	 	def hasNext = idx < len
	  }
  }

  def apply(x: Int, y: Int) =
    if (isInBound(x, y)) values(x + y * height) else defaultValue

  def update(x: Int, y: Int, e: T) {
    assume(isInBound(x, y))
    values(x + y * height) = e
  }

  def isInBound(x: Int, y: Int) = 0 <= x && x < width && 0 <= y && y < height
  
  override def toString = values.grouped(width).map(_.mkString + "\n").mkString
}

しかし、それでもいろいろ言い訳をばw

まず、mutableのSetを使ったのは、そっちの方が速かったから。最初はもちろんimmutable使ってたんだが、mutableにしたら0.2秒くらい速くなった(もともと1.4秒だったのが1.2秒になった)。実は、そのあといろいろ書き換えて中間の塗りつぶしもすっ飛ばして1秒切るくらいになったんだけれども、なんかTwitterで「90msで実行できた」とかいう人がいたのでそれは書かない。

あと、neighborsメソッド内でfoldLeft (/:)使ってるのは、ついうっかりHaskell使ってた癖が出ただけ。

それから、インデント一部ぐちゃぐちゃなのは、エディタのインデントの挿入がScalaの一般的な2文字インデントの書き方と異なってたから。

ほかにも何かおかしいところあったら、指摘していただけると幸いです。


それにしても、初めてのScalaで、しかもScala 2.7の情報しかほとんどない状態でScala 2.8を使うってのは大変だった。特にコレクションフレームワークが。

2010年02月25日

Google DevFest 2010のクイズ(1)

Google DevFest 2010の参加者選別のために出題されたクイズ(その1)。

数字を漢数字に変換するアプリケーションを作ってください。

http://[あなたのアプリケーションのURL]?n=[数字] にアクセスすると、 text/plain でその数字を漢数字に変換した結果を返すウェブサーバを作ってください。ただし、漢字はすべて以下の表の通りにアルファベットに変換して出力してください。

  • 零 → N
  • 一 → B
  • 二 → G
  • 三 → S
  • 四 → Q
  • 五 → P
  • 六 → Y
  • 七 → R
  • 八 → K
  • 九 → M
  • 十 → A
  • 百 → J
  • 千 → Z
  • 万 → H
  • 億 → L
  • 兆 → F

注意:

  • 「千万」「千億」「千兆」の前に「一」がつかないようにしてください。
  • 入力は負でない整数で、大きさは高々9999兆9999億9999万9999までです。
  • 標準のポート番号80番のみ扱えます。

これはJavaで書いた。

package com.appspot.devfestquizkanji;

import java.util.HashMap;
import java.util.Map;

public class ToKanjiNumConverter {

	static {
		char[] kanjis = "零一二三四五六七八九十百千万億兆".toCharArray();
		char[] alpha = "NBGSQPYRKMAJZHLF".toCharArray();
		assert kanjis.length == alpha.length;
		Map<Character, Character> kToA = new HashMap<Character, Character>();
		for (int i = 0; i < kanjis.length; i++)
			kToA.put(kanjis[i], alpha[i]);
		kanjiToAlpha = kToA;
	}

	private ToKanjiNumConverter() {
	}

	private static final Map<Character, Character> kanjiToAlpha;

	private static final String[] manUnit = { "", "万", "億", "兆" };
	private static final String[] senUnit = { "", "十", "百", "千" };
	private static final String[] numKanji = { "", "一", "二", "三", "四", "五", "六", "七", "八", "九" };

	public static String toKanjiNum(long num) {
		if (num == 0)
			return "零";

		return manUnit(num, 0).toString();
	}

	private static StringBuilder manUnit(long num, int unitNo) {
		StringBuilder sen = sen((int)(num % 10000), 0);
		String unit = manUnit[unitNo];

		if (sen.toString().isEmpty())
			unit = "";

		long next = num / 10000;
		if (0 < next) {
			return manUnit(next, unitNo + 1).append(sen).append(unit);
		} else {
			return new StringBuilder().append(sen).append(unit);
		}
	}

	private static StringBuilder sen(int num, int unitNo) {
		String digit = numKanji[num % 10];
		String unit = senUnit[unitNo];

		if (digit.isEmpty())
			unit = "";

		// "一百"→"百"
		if (digit.equals("一") && !unit.isEmpty())
			digit = "";

		int next = num / 10;
		if (0 < next) {
			return sen(next, unitNo + 1).append(digit).append(unit);
		} else {
			return new StringBuilder().append(digit).append(unit);
		}
	}
	
	public static String toAlphaReps(String kanji) {
		StringBuilder sb = new StringBuilder();
		for (char k : kanji.toCharArray()) {
			sb.append(kanjiToAlpha.get(k));
		}
		return sb.toString();
	}
}

メソッド呼び出し時に単位の配列の添字をパラメータで受け渡すんじゃなくて、本当は単位のリストを受け渡したかった。