ファイルを日付別フォルダに分類する

使おうとする度にどこ行ったっけ?ってなるのでここに貼り付けておく. 4種類あるけど、やってることは同じ. まあ、でもどれが好きって聞かれると3つ目なのかなあ.

ls | % { 
  if (-not $_.PSIsContainer) {
    $dir = $_.LastWriteTime.ToString("yyyyMMdd")
    if (-not (Test-Path $dir)) {
      mkdir $dir
    }
    move-item $_ $dir
  }
}
dir | % { 
  if ($_.PSIsContainer) { return $null; }
  $dir = $_.LastWriteTime.ToString("yyyyMMdd")
  if (-not (Test-Path $dir)) {
    mkdir $dir
  }
  move-item $_ $dir
}
dir | ? { -not $_.PSIsContainer } | % {
  $dir = $_.LastWriteTime.ToString("yyyyMMdd")
  if (-not (Test-Path $dir)) {
    mkdir $dir
  }
  move-item $_ $dir
}
filter Skip-Container { if (-not $_.PSIsContainer) { return $_ } }
dir | Skip-Container | % {
  $dir = $_.LastWriteTime.ToString("yyyyMMdd")
  if (-not (Test-Path $dir)) {
    mkdir $dir
  }
  move-item $_ $dir
}

HTML メールを送信する

さりげに一番難しかったのは [System.Net.Mime.MediaTypeNames+Text]::Html だった気がするのがw
クラス内クラスはこんな書き方になるようである.

$client = new-object System.Net.Mail.SmtpClient
$client.Host = "smtp.example.com"
$client.Port = 25
$client.DeliveryFormat = [System.Net.Mail.SmtpDeliveryFormat]::SevenBit

$message = new-object System.Net.Mail.MailMessage("From Address <foo@example.com>", "To Address <bar@example.com>")
$message.Subject = "HTML mail test"
$message.BodyEncoding = [System.Text.Encoding]::UTF8
$message.BodyTransferEncoding = [System.Net.Mime.TransferEncoding]::QuotedPrintable
$message.Body = "plain part"
$message.IsBodyHtml = $false

$view = [System.Net.Mail.AlternateView]::CreateAlternateViewFromString("<b>html part</b>", [System.Text.Encoding]::UTF8, [System.Net.Mime.MediaTypeNames+Text]::Html)
$view.TransferEncoding = [System.Net.Mime.TransferEncoding]::QuotedPrintable

$message.AlternateViews.Add($view)
$client.Send($message)

PowerShell 2 でも .NET Framework 4 を使う

PowerShell 3 からは PowerShell .NET 4 で動作するようだけど、Windows XPPowerShell 3 をインストール出来ないという・・・.
でも、exe.config を書けば、.NET 4 で動かせるよという話.

PS> cat $env:windir\system32\windowspowershell\v1.0\powershell.exe.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
    </startup>
</configuration>
PS> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      2.0
PSCompatibleVersions           {1.0, 2.0}
BuildVersion                   6.0.6002.18111
PSRemotingProtocolVersion      2.1
WSManStackVersion              2.0
CLRVersion                     4.0.30319.296
SerializationVersion           1.1.0.1

.NET メソッドの string 引数に null を渡す

PowerShell は $null を勝手に "" に変換して、.NET メソッドの string 引数に渡すというお節介をしてくれるので、メソッドの仕様によっては困ったことになります.
PowerShell 3 からは、そのような場合には [System.Management.Automation.Language.NullString]::Value を指定すればよくなりました.
そこで、PowerShell 2 用に同等の機能を提供するコードを書いたのが以下です.

PS> Add-Type -TypeDefinition @"
>> namespace System.Management.Automation.Language
>> {
>>     public sealed class NullString
>>     {
>>         private static volatile NullString instance;
>>         private static object syncRoot = new object();
>>
>>         private NullString() { }
>>
>>         public static NullString Value
>>         {
>>             get
>>             {
>>                 if (instance == null)
>>                 {
>>                     lock (syncRoot)
>>                     {
>>                         if (instance == null)
>>                             instance = new NullString();
>>                     }
>>                 }
>>                 return instance;
>>             }
>>         }
>>
>>         public override string ToString()
>>         {
>>             return null;
>>         }
>>     }
>> }
>> "@
>>
PS> [string][System.Management.Automation.Language.NullString]::Value -eq $null
True

逆FizzBuzz問題(linq)

asq を入れて、ちょっと書きなおしてみた.
order_by した後に、count して first すると empty sequence が出るという C# で出ない挙動がちょっとアレです.

from asq.initiators import query
from itertools import count, cycle, islice, ifilter

def FizzBuzz():
    seq = [None, None, "Fizz", None, "Buzz", "Fizz", None, None, "Fizz", "Buzz", None, "Fizz", None, None, "FizzBuzz"]
    for i, e in enumerate(cycle(seq)):
        if e == None:
            yield i + 1
        else:
            yield e

def InverseFizzBuzz(seq):
    count = len(seq)
    def work(start):
        i = start
        c = count
        a = []
        for x in query(FizzBuzz()).skip(start - 1):
            if c == 0:
                l = i - start
                return (a, l, range(start, start + l))
            if not isinstance(x, int):
                a += [x]
                c -= 1
            i += 1
    candidates = query([3, 5, 6, 9, 10, 12, 15]).select(work).where(lambda x: x[0] == seq).to_list()
    if len(candidates) == 0:
        return None
    else:
        return query(candidates).order_by(lambda x: x[1]).first()[2]

assert InverseFizzBuzz(["Fizz"]) == range(3, 4)
assert InverseFizzBuzz(["Buzz"]) == range(5, 6)
assert InverseFizzBuzz(["Fizz", "Buzz"]) == range(9, 11)
assert InverseFizzBuzz(["Buzz", "Fizz"]) == range(5, 7)
assert InverseFizzBuzz(["Fizz", "Buzz", "Fizz"]) == range(3, 7)
assert InverseFizzBuzz(["Buzz", "Fizz", "Buzz"]) == None
assert InverseFizzBuzz(["Fizz", "Fizz"]) == range(6, 10)
assert InverseFizzBuzz(["Fizz", "Fizz", "Buzz"]) == range(6, 11)

逆FizzBuzz問題

最近ぞっぷりと linq に染まっていたので、関数の入れ子をより気持ち悪く感じて困る. python にも組み込み linq を誰か.

from itertools import count, cycle, islice, ifilter

def FizzBuzz():
    seq = [None, None, "Fizz", None, "Buzz", "Fizz", None, None, "Fizz", "Buzz", None, "Fizz", None, None, "FizzBuzz"]
    for i, e in enumerate(cycle(seq)):
        if e == None:
            yield i + 1
        else:
            yield e

def skip(itrerable, count):
    return islice(itrerable, count, None)

def take(itrerable, count):
    return islice(itrerable, count)

def InverseFizzBuzz(seq):
    def work(start, count):
        i = start
        c = count
        a = []
        for x in skip(FizzBuzz(), start - 1):
            if c == 0:
                l = i - start
                return (a, l, range(start, start + l))
            if not isinstance(x, int):
                a += [x]
                c -= 1
            i += 1
    start_list = { "Fizz": [3, 6, 9, 12], "Buzz": [5, 10], "FizzBuzz": [15] }
    if len(seq) == 0:
        return None
    if not (seq[0] in start_list):
        return None
    candidates = list(ifilter(lambda x: x[0] == seq, [work(i, len(seq)) for i in start_list[seq[0]]]))
    if len(candidates) == 0:
        return None
    else:
        return list(sorted(candidates, cmp = lambda x, y: x[1] - y[1]))[0][2]

assert InverseFizzBuzz(["Fizz"]) == range(3, 4)
assert InverseFizzBuzz(["Buzz"]) == range(5, 6)
assert InverseFizzBuzz(["Fizz", "Buzz"]) == range(9, 11)
assert InverseFizzBuzz(["Buzz", "Fizz"]) == range(5, 7)
assert InverseFizzBuzz(["Fizz", "Buzz", "Fizz"]) == range(3, 7)
assert InverseFizzBuzz(["Buzz", "Fizz", "Buzz"]) == None
assert InverseFizzBuzz(["Fizz", "Fizz"]) == range(6, 10)
assert InverseFizzBuzz(["Fizz", "Fizz", "Buzz"]) == range(6, 11)

追記: 無駄があった・・・

def InverseFizzBuzz(seq):
    def work(start, count):
        i = start
        c = count
        a = []
        for x in skip(FizzBuzz(), start - 1):
            if c == 0:
                l = i - start
                return (a, l, range(start, start + l))
            if not isinstance(x, int):
                a += [x]
                c -= 1
            i += 1
    candidates = list(ifilter(lambda x: x[0] == seq, [work(i, len(seq)) for i in [3, 5, 6, 9, 10, 12, 15]]))
    if len(candidates) == 0:
        return None
    else:
        return list(sorted(candidates, cmp = lambda x, y: x[1] - y[1]))[0][2]

CRC32

RFC 1952 - GZIP file format specification version 4.3 に載っているコードをベタ移植しただけです.

crc_table = [None] * 256

def make_crc_table():
  for n in range(256):
    c = n
    for k in range(8):
      if c & 1:
          c = 0xedb88320 ^ (c >> 1)
      else:
          c >>= 1
    crc_table[n] = c

def update_crc(crc, s):
  c = crc ^ 0xffffffff
  if crc_table[0] == None:
    make_crc_table()
  for ch in s:
    c = crc_table[(c ^ ord(ch)) & 0xff] ^ (c >> 8)
  return c ^ 0xffffffff

def crc(s):
  return update_crc(0, s)

RFC 2083 - PNG (Portable Network Graphics) Specification Version 1.0 だと 0xffffffff の挿入位置が微妙に違うのね(大差ないけど

crc_table = [None] * 256

def make_crc_table():
  for n in range(256):
    c = n
    for k in range(8):
      if c & 1:
          c = 0xedb88320 ^ (c >> 1)
      else:
          c >>= 1
    crc_table[n] = c

def update_crc(c, s):
  if crc_table[0] == None:
    make_crc_table()
  for ch in s:
    c = crc_table[(c ^ ord(ch)) & 0xff] ^ (c >> 8)
  return c

def crc(s):
  return update_crc(0xffffffff, s) ^ 0xffffffff