彼女と二人で行ってきた。
ごちそうさまでした!来年もよい年でありますように。
設問は「監視員が正方形プール(10メートル四方)の角にいて、プールの縁は秒速2メートル、水中は同1メートルで移動する。スタートからプール内で最も移動に時間のかかる場所まで、何秒で着くか」というもの。
まず、監視員の位置を原点Oとする。残りの角を、Oから反時計回りにA, B, Cとする。そして、点Aがx軸上に、点Cがy軸上にあるように座標軸を設定する。
プールは直線y=xに関して対称なので、「最も移動に時間のかかる場所」は対角線OB上にあることは自明。(追記)そんなことはないですね。訂正。
ということで早速OB上の点を探し始める……とややこしくなる。なぜややこしいかというと、監視員がプールの縁を歩くから。角を曲がってさらに歩いたりするケースも考えないといけない。ややこしい。
すこしでも簡単に考えたいので、とっかかりとして角を曲がらない泳ぎ方だけ考えたい。OからAまで歩くと5秒かかる。じゃあ、5秒まででプールのどの地点まで泳げるだろうか?
以下、プールの対象性を考慮して、三角形OABに着目して考えることにしよう。
5秒でどこまで泳げるか、を考えるには少し発想の転換が必要である。たとえば歩いたり泳いだりして5秒でX地点までたどり着いたとしよう。でももし、もっと速くX地点にたどり着くルートがあるとすれば、そのX地点は5秒で泳げる限界ラインよりも手前だということになる。逆に考えると、X地点まで最も速くたどり着けるルートを探してやる必要がある。
ということで、プールの任意の点までもっとも速くたどり着くルートを探そう。ルートというのは、要するにどこからプールに飛び込むかの問題だ。なぜなら、飛び込み地点が決まれば、あとは一直線に泳ぐのが一番速いからだ。もっと言うと、プールに飛び込む角度をどうすると一番速いかという問題になる。
三角形OAB内に点Pをとり、Pからx軸におろした垂線がx軸と交わる点をHとする。監視員がプールに飛び込む点をQとする。このときPまで最も速くたどり着くのはQがどこにあるときか。
飛び込む角度だけが問題になるので、計算を単純にするためOP=1としてしまおう。角POHはφとしておく。で、問題の角PQHをθとする。
点Oから点Qまで歩き、点Qから点Pまで泳いだときにかかる時間Tはφとθの関数となる。φを固定すると、θの値がどのようになったとき、時間Tが最小になるだろうか?
ということで、π/4 (=45°)π/3 (=60°)の角度で泳ぐのが最速とわかる。
(以下、あとで修正する)
この問題を解決するには、インターネットに接続するか、ルート証明書の更新コンポーネントを無効にする必要があります。
.NET Frameworkに用意されている各種ネットワークアクセスクラス(WebClient, WebRequest/WebResponse, SmtpClient)ではなく、あえて低レベルのTcpClientを使うときもあります。そんなときのための注意点を。
ストリームの終わりをどのように判断するか、これは悩ましいところですが、NetworkStream.DataAvailableを使うのはやめておいたほうがいいです。送信元がデータを送るとき、いつでも全部まとめて送信するわけではないからです。たとえば、ある瞬間にはDataAvailableがfalseになっていても、1秒後にはtrueになったりします。「じゃあ、NetworkStream.DataAvailableがfalseならしばらく待ってみて、それでもfalseだったら読み取り終了にすればいいじゃないか」というのは筋が悪いです。いったい何秒まてば十分なのかは誰にも分からないですし、それに無駄な待ちが必ず発生することになりますしね。
結局のところ、規定の長さに達するまでStream.Readするか、またはデータの区切り(CrLfなど)が届くまでStream.ReadByteを繰り返すか、そのどちらかを選択するのがいいでしょう。もちろん、どちらを選ぶかはプロトコル次第ということになります。そのように実装すると、データの終わりを受け取るまではブロックすることになります。ですので、適切なタイムアウトは必須でしょう。
データの区切りが(CrLfなど)決まっている場合はStream.ReadByteを繰り返せと書きましたが、反論が2つ考えられます。1つ目は「区切りがCrLfなら、StreamReaderを使ってReadLineした方が簡単」という意見と、もうひとつは「1バイトずつStream.ReadByteするのは遅い」という意見です。
最初の意見ですが、もし送られてくるデータが全て文字列で、しかも最初から最後までエンコーディングが固定されているのなら、そのエンコーディングに対応したStreamReaderを使うのもいいでしょう。ですが、そうでないなら、つまりエンコーディングはデータを受け取るまで分からないとか、あるいは途中からは生のバイト列が必要になるとか、そういう場合はStreamReaderは向いていません。StreamReaderはストリームの先読みをして、内部でバッファリングします。ですので、「次の行からはエンコーディングの異なるStreamReaderを使う」とか「次の行からはバイト列」といったときに、ストリームの位置は次の行の先頭よりも先に進んでいます。要するに、StreamReaderを使った場合は、もう元のStreamは使えないと考えるべきなのです。(天邪鬼な人はISO-8859-1エンコーディングを持ち出すかもしれませんが、その議論はここではしません。「できるけど、ムダが多い」とだけ述べておきます。)
もうひとつの意見、つまりReadByteは遅いという意見ですが、それは生のNetworkStreamを1バイトずつ読み取るから非効率なので、そういう時はBufferedStreamでラップしてやればいいのです。もちろん、BufferedStreamは内部でバッファリングしますから、BufferedStreamを使った場合は、もうもとのStreamは使えないことに注意しなければなりませんが、大きな問題にはならないでしょう。
class Program
{
static void Main(string[] args)
{
GenericFunctions.CurriedFunc<int, int, double> cf =
Curry.Create<int, int, double>(Devide);
Console.WriteLine(cf(3)(2));
Console.ReadLine();
}
public static double Devide(int x, int y)
{
return (double)x / (double)y;
}
}
class Curry
{
public static GenericFunctions.CurriedFunc<T0, T1, U> Create<T0, T1, U>
(GenericFunctions.Func<T0, T1, U> f)
{
return delegate(T0 arg0)
{
return delegate(T1 arg1)
{
return f(arg0, arg1);
};
};
}
public static GenericFunctions.CurriedFunc<T0, T1, T2, U> Create<T0, T1, T2, U>
(GenericFunctions.Func<T0, T1, T2, U> f)
{
return delegate(T0 arg0)
{
return delegate(T1 arg1)
{
return delegate(T2 arg2)
{
return f(arg0, arg1, arg2);
};
};
};
}
//以下略
}
namespace GenericFunctions
{
public delegate U Func<T0, U>(T0 arg0);
public delegate U Func<T0, T1, U>(T0 arg0, T1 arg1);
public delegate U Func<T0, T1, T2, U>(T0 arg0, T1 arg1, T2 arg2);
public delegate U Func<T0, T1, T2, T3, U>(T0 arg0, T1 arg1, T2 arg2, T3 arg3);
//以下略
public delegate Func<T1, U> CurriedFunc<T0, T1, U>(T0 arg0);
public delegate CurriedFunc<T1, T2, U> CurriedFunc<T0, T1, T2, U>(T0 arg0);
//以下略
}
class Program
{
static void Main(string[] args)
{
GenericFunctions.Func<int, double> curried1 =
Curry.Bind<int, int, double>(Devide, 3);
GenericFunctions.Func<double> curried2 =
Curry.Bind(curried1, 2);
Console.WriteLine(curried2());
Console.ReadLine();
}
public static double Devide(int x, int y)
{
return (double)x / (double)y;
}
}
public class Curry
{
public static GenericFunctions.Func<U> Bind<T0, U>
(GenericFunctions.Func<T0, U> f, T0 arg0)
{
return delegate()
{
return f(arg0);
};
}
public static GenericFunctions.Func<T1, U> Bind<T0, T1, U>
(GenericFunctions.Func<T0, T1, U> f, T0 arg0)
{
return delegate(T1 arg1)
{
return f(arg0, arg1);
};
}
public static GenericFunctions.Func<T1, T2, U> Bind<T0, T1, T2, U>
(GenericFunctions.Func<T0, T1, T2, U> f, T0 arg0)
{
return delegate(T1 arg1, T2 arg2)
{
return f(arg0, arg1, arg2);
};
}
// 以下略
}
namespace GenericFunctions
{
public delegate U Func<U>();
public delegate U Func<T0, U>(T0 arg0);
public delegate U Func<T0, T1, U>(T0 arg0, T1 arg1);
// 以下略
}
class Program
{
static void Main(string[] args)
{
Curry<double> c = new Curry<double>();
Program p = new Program();
Curry<double>.CurriedFunc cf = c.Make(p, "Devide");
Console.WriteLine(cf(2)(3));
Console.ReadLine();
}
public double Devide(int x, int y)
{
return (double)x / (double)y;
}
}
class Curry<T>
{
public delegate T BoundFunc(params object[] args);
public delegate BoundFunc CurriedFunc(params object[] args);
public CurriedFunc Make(object instance, string methodName)
{
MethodInfo mi = instance.GetType().GetMethod(methodName);
return delegate(object[] pre)
{
return delegate(object[] post)
{
ArrayList al = new ArrayList();
al.AddRange(pre);
al.AddRange(post);
return (T)mi.Invoke(instance, al.ToArray());
};
};
}
}