不買

抗議の意思としての不買というのであれば、対象企業の製品を徹底的に避けなくたっていいんでないかな。そりゃ、三菱、東芝、日立の製品を使っている企業、製品まで避けたほうが効果的ではありましょうが、その場合、JR も電力会社もアウトなのでなかなか生きづらいかと。場合によっては上下水や都市ガスにも関係しているかも。
ま、それはそれとして。

ドイツでは、グリーンな社会を実現するために、あえて価格的には高くてもエコな製品を選択するというムーブメントがあるし、電力についても原子力発電をしていない会社から買うという選択ができる。日本では、前者は可能だが、後者は不可能だ。これを、制度上の欠陥だと考える人もいるだろう。私もその一人だ。そうであるならば、ほかの手段でその欠陥を克服することができるとすれば、その手段を実行することは、主権者としての一つの意思表示の方法である。そこで、私は、そのように考える人に対して、原子炉メーカーである三菱、東芝、日立が原発関連事業から撤退するまではこの三社の製品を購入しないこと、それを三者に対し意思表明することを呼び掛けます。新政権において、脱原発の動きが停滞することが予見される中、主権者としてできることは全てしなければならない時期が来ている。つまり、できることを直ちに始めよう、ということです。ほかにもいい方法があるかもしれない。でも、同時多角的に行ったほうが効果があるはずだ。そして、肝心なことだが、原発推進派がこれまでにしてきたことを考えるならば、不買運動のようなささやかな抵抗を躊躇する必要などあるだろうか。

原子力発電をしていない会社から電力を買うといった選択の自由がないの制度の欠陥に対して、原子炉メーカに原子力事業から撤退するよう抗議するという論理なんだろうか。よくわからん。メーカも電力会社から発注がないと原子炉もタービンも作れないので、抗議するなら電力会社のほうが筋が良いというか、そもそもこのご時世に新規の原子炉なんて発注できるわけないだろとか、いまある原子力発電システムの保守はどうするのかとか突っ込みどころ満載ではある。

おまけ。

10万人が3社に対し不買の意思を表明したら、原子炉メーカーであり続けることを再検討せざるを得ない

仮に原子力事業から撤退することで、10万人が平均20万円ほど製品を購入するとすると合計200億円ほど。3社の連結売上を合計するとだいたい19兆円くらいなので、まぁ、ざっくり全体の売上の0.1%とかその程度だ。

フルパスから相対パスを求める

こんどは Scheme で。

(use srfi-13)

(define (init lst)
  (cond ((null? lst) (error "init: empty list"))
        ((null? (cdr lst)) '())
        (else
         (cons (car lst) (init (cdr lst))))))

(define (relative-path-list target base)
  (cond ((null? base) target)
        ((null? target) (map (lambda (x) "..") base))
        ((equal? (car base) (car target)) (relative-path-list (cdr target) (cdr base)))
        (else
         (append
          (map (lambda (x) "..") base)
          target))))

(define (valid-path? path)
  (and (> (string-length path) 0)
       (equal? (string-ref path 0) #\/)
       (not (string-index path #[\\?*:|"<>]))))

(define (relative-path target base)
  (if (and (valid-path? target) (valid-path? base))
      (let ((relative-path-components
             (relative-path-list
              (cdr (string-split target #/\/+/))
              (init (cdr (string-split base #/\/+/))))))
        (string-join
         (if (and (not (null? relative-path-components))
                  (equal? (car relative-path-components) ".."))
             relative-path-components
             (cons "." relative-path-components))
         "/"))
      (error "invalid path")))

(use gauche.test)
(test-start "relative-path")

(test* "a file in the same directory"
       "./to.txt"
       (relative-path "/aaa/bbb/to.txt" "/aaa/bbb/from.txt"))

(test* "a file in the parent directory"
       "../to.txt"
       (relative-path "/aaa/to.txt" "/aaa/bbb/from.txt"))

(test* "a file in the child directory"
       "./ccc/to.txt"
       (relative-path "/aaa/bbb/ccc/to.txt" "/aaa/bbb/from.txt"))

(test* "a file through the grandparent directory"
       "../ccc/ddd/to.txt"
       (relative-path "/aaa/ccc/ddd/to.txt" "/aaa/bbb/from.txt"))

(test* "a file through the root directory"
       "../../ddd/ccc/to.txt"
       (relative-path "/ddd/ccc/to.txt" "/aaa/bbb/from.txt"))

(test* "a file from the directory"
       "../ddd/to.txt"
       (relative-path "/aaa/ddd/to.txt" "/aaa/bbb/"))

(test* "a directory from the directory"
       "../ccc/"
       (relative-path "/aaa/ccc/" "/aaa/bbb/"))

(test* "the same path"
       "./ccc.txt"
       (relative-path "/aaa/bbb/ccc.txt" "/aaa/bbb/ccc.txt"))

(test* "passing empty path"
       *test-error*
       (relative-path "/bbb/to.txt" ""))

(test* "invalid character for filename"
       *test-error*
       (relative-path "/bbb/to.txt" "/aaa/g*"))

(test* "path does not start with slash"
       *test-error*
       (relative-path "./bbb/to.txt" "aaa/bbb/from.txt"))

(test* "consecutive slash"
       "./to.txt"
       (relative-path "/////aaa//////bbb///////to.txt"
                      "//aaa///bbb////from.txt"))

(test-end)

ある金額になるコインの組み合わせ

こんなんかな。

coinCombinations :: Int -> [Int] -> [[Int]]
coinCombinations total coins
  | total == 0    = [[]]
  | null coins    = []
  | total < first = firstUnusedList
  | otherwise     = firstUsedList ++ firstUnusedList
      where
        first           = head coins
        firstUsedList   = (map (first:) (coinCombinations (total - first) coins))
        firstUnusedList = coinCombinations total (tail coins)

Python だと Generator ですかね。

def coinCombinations(total, coins):
    if total == 0:
        yield []
        return
    elif not coins:
        return

    for pattern in coinCombinations(total, coins[1:]):
        yield pattern

    if total >= coins[0]:
        for pattern in coinCombinations(total - coins[0], coins):
            yield coins[0:1] + pattern

文字列を先頭から見て同じところまで除去

ふむ。Haskell だとどうなるか。
素直に書くとこうかな。

sameAll :: Eq a => [a] -> Bool
sameAll (x:xs) = all (x==) xs
sameAll []     = True

dropCommonPrefix :: Eq a => [[a]] -> [[a]]
dropCommonPrefix strings | any null strings           = strings
                         | sameAll (map head strings) = dropCommonPrefix (map tail strings)
                         | otherwise                  = strings

こゆのもありか。面倒なので、longestCommonPrefix はソートして先頭と末尾だけ比較。(なので、対象が Ord クラスに限られる)

longestCommonPrefix :: Ord a => [[a]] -> [a]
longestCommonPrefix lst = map fst $ takeWhile eq $ zip (minimum lst) (maximum lst)
  where
    eq (x, y) = x == y

dropCommonPrefix :: Ord a => [[a]] -> [[a]]
dropCommonPrefix lst = map (drop longestCommonPrefixLength) lst
  where
    longestCommonPrefixLength = length $ longestCommonPrefix lst

Python だとこんな感じか。

longestCommonPrefix :: Ord a => [[a]] -> [a]
longestCommonPrefix lst = map fst $ takeWhile eq $ zip (minimum lst) (maximum lst)
  where
    eq (x, y) = x == y

dropCommonPrefix :: Ord a => [[a]] -> [[a]]
dropCommonPrefix lst = map (drop longestCommonPrefixLength) lst
  where
    longestCommonPrefixLength = length $ longestCommonPrefix lst
def dropCommonPrefix(*strings):
    def commonPrefixLength(xs, ys):
        maxLen = min(len(xs), len(ys))
        for i in xrange(maxLen):
            if xs[i] != ys[i]: return i
        return maxLen

    cpl = commonPrefixLength(min(strings), max(strings))

    return tuple(s[cpl:] for s in strings)

初めての Visual Basic .NET

そいや、今日、普通に書いてたけど VB.NET でプログラミングするのは初めてだったな。
とりあえず、static でなく、Shared なのは良いと思った。
そいや、ドキュメントコメントには普通に Nothing と書いていいのかな。言語によって、null だったり Nothing だったりするからなんだか微妙だ。

変なコード その2

相変わらず ASP

Sub MakeButton(Flg)
    If Flg = True Then
       Response.Write "<form action=""foobar.asp"">"
       Response.Write "<input type=""hidden"" name=""foo"" value=""1"">"
       ' 以下略
    End If

    ' なんかボタンを作る処理

    If Flg = False Then
       Response.Write "</form>"
    End If
    
End Sub

なんというか、こういうのを是とする感覚というのは理解しがたいなぁ。
ダメなものをダメだと思うも、1つのスキルではあるのだが、いちいち指摘するしかないですかね。

変なコード

なんか変なコードを見た。ちなみ VBScript(in ASP)。

Set Dict = CreateObject("Scripting.Dictionary")
For Each Line In Lines
    Ary = Split(Line, "=")
    For I = 1 To UBound(Ary) - 1
        Ary(1) = Ary(1) & "=" & Ary(1 + I)
    Next
    Dict(Ary(0)) = Ary(1)
Next

なんだかいろいろひどい。

ループの範囲が変

ループ範囲も、Ary(1 + I) で参照するのも、必然性がなく、良く分からない。せめて、2 To UBound(Ary) の方が、まだ分かりやすいのではないか。

Set Dict = CreateObject("Scripting.Dictionary")
For Each Line In Lines
    Ary = Split(Line, "=")
    For I = 2 To UBound(Ary)
        Ary(1) = Ary(1) & "=" & Ary(I)
    Next
    Dict(Ary(0)) = Ary(1)
Next

Ary の役割が分かりにくく、曖昧

分割した配列に直接代入しているため、Ary の役割がよく分からなくなっている。
素直に別の変数を用意した方が良い。

Set Dict = CreateObject("Scripting.Dictionary")
For Each Line In Lines
    Ary = Split(Line, "=")
    Key = Ary(0)
    Value = ""
    For I = 1 To UBound(Ary)
        Value = Value & "=" & Ary(I)
    Next
    Dict(Key) = Value
Next

Split の仕様を知らない

まぁ、Split に分割数を指定すればいいよねぇ。

Set Dict = CreateObject("Scripting.Dictionary")
For Each Line In Lines
    Ary = Split(Line, "=", 2)
    Dict(Ary(0)) = Ary(1)
Next