転職を繰り返したサラリーマンの多趣味ブログ

30才未経験でSEに転職した人の多趣味ブログ

【技術書メモ】C#プログラミングのイディオム / 定石&パターン⑤

今回は文字列操作編!
そういえば、会社の研修でJavaを勉強し始めた当初、文字列の比較のやり方がわからなくて、かなりつまづいたな。
結局、10才近く年下の子にやり方教えてもらったのは、いい思い出です。

文字列の比較

C#での文字列比較をやっていきます。
まず、これが一番シンプルな比較方法。大文字小文字も区別される。

if (str1 == str2)


次が、大文字小文字の区別なく比較する方法。
3つ目の引数のtrueが、大文字小文字の区別なく比較することを表している。

if (String.Compare(str1, str2, ignoreCasetrue) == 0)


ひらがなとカタカナを区別なく、比較することも可能。

var str1 = "カステラ";
var str2 = "かすてら";
var cultureInfo = new CultureInfo("ja-JP");
if(String.Compare(str1, str2, cultureInfo, CompareOptions.IgnoreKanaType) == 0)


全角半角の区別なく比較するには

var str1 = "HTML5";
var str2 = "HTML5";
var cultureInfo = new CultureInfo("ja-JP");
if(String.Compare(str1, str2, cultureInfo, CompareOptions.IgnoreWidth) == 0)


また、String.Compareメソッドの第3引数は論理演算子で繋げることも可能で

var str1 = "Html5";
var str2 = "HTML5";
var cultureInfo = new CultureInfo("ja-JP");
// 大文字小文字と全角半角の区別なく比較
if (string.Compare(str1, str2, cultureInfo, CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase) == 0)

文字列の判定

nullあるいは空文字かを判定。これはよく使いそう。

if(String.IsNullOrEmpty(str))


空文字かを判定する。

if(str == String.Empty)


nullか空文字か空白文字列かを判定する。空白文字列も判定できるのか。

if(String.IsNullOrWhiteSpace(str))


指定した文字列で始まっているか。

if(str.StartsWith("Visual"))


指定した文字列で終わっているか。

if(str.EndsWith("Exception"))


指定した部分文字列が含まれているか。

if(str.Contains("Program"))


上記は文字列だったが、文字が含まれているかを調べるには、LINQを使う。

var target = "The quick brown fox jumps over the lazy dog.";
var contains = target.Contains('b');


次もまたLINQ。条件を満たしている文字が含まれているか。

var target = "C# Programming";
// 小文字が含まれているかチェック
var isExists = target.Any(c => Char.IsLower(c));


上記はどれか1文字でも条件を満たしているかどうかのチェックだったが、今度はすべての文字が条件を見てしているかをチェックする。

var target = "141421536";
// 数字かどうかをチェック
var isAllDigits = target.All(c => Char.IsDigit(c));

文字列の検索と抽出

部分文字列を検索し、その位置を求める。

var target = "NameList;BirthdayList";
var index = target.IndexOf("BirthdayList");


文字列の一部を取り出す。

// 指定した位置から最後までを部分文字列として取り出す
var value = target.Substring(index);


取り出す長さを指定することもできる.

var value = target.Substring(index, endIndex - startIndex);

文字列の変換

文字列の前後の空白を取り除く。

var replaced = target.Trim();


文字列の前後片方だけの空白を取り除く。

var replaced1 = target.TrimStart();
var replaced2 = target.TrimEnd();


指定した位置から任意の数の文字を削除する。

var result = target.Remove(5, 3);


指定した位置に文字列を挿入する。

var target = "01234";
// 01abc234が出力される
var result = target.Insert(2, "abc");


文字列の一部を別の文字列で置き換える。

var target = "I hope you could come to with us.";
// I wish you could come to with us.と出力される。
var replaced = target.Replace("hope", "wish");


小文字を大文字に変換する。

var replaced = target.ToUpper();


大文字を小文字に変換する。

var replaced = target.ToLower();

文字列の連結と分割

文字列の連結は、+演算子を使う。これは、Javaと一緒。

var name = "山田" + "太郎";


文字列の末尾に追加する。

var name = "やまだ";
name += "先生";


指定した区切り文字で文字列配列を連結する。

var languages = new[]{"C#", "Java", "VB", "Ruby", };
var separator = ",";
// C#, Java, VB, Rubyと出力される
var result = String.Join(separator, languages);


指定した文字で文字列を分割する。
>|cs|
var target = "I hope you could come to with us.";
// 空白文字で分割する
string[] words = target.Split(' ');


なお、空の配列要素を含めないようにするには、以下の書き方が必要。

var target = "I hope you could come to with us.";
// 空白文字で分割する
string[] words = target.Split(new [] {' ', '.'}, StringSplitOptions.RemoveEmptyEntries);


文字列は基本的に普変オブジェクトである。そのため、文字連結を行うたびに、新たなインスタンスを生成していることになる。
そこで、StringBuilderを使った文字連結がある。StringBuilderクラスは、インスタンス生成時に、文字列を格納する領域を確保し、その領域を超えて文字列を追加しようとした場合は、自動的に容量が増えていく。そのため、無駄なインスタンスを生成する必要がない。

var sb = new StringBuilder();
foreach (var word in GetWords()) {
    sb.Append(word);
}
// 文字列に変換
var text = sb.ToString();

その他の文字列操作

文字列から文字を1つずつ取り出すには、foreach文を使う。

foreach (var c in str) { }


文字配列から文字列を生成する。

var chars = new char[] {'P', 'r', 'o', 'g', 'r', 'a', 'm' };
var str = new string(chars);


数値を文字列に変換するには、ToStringメソッドを使用する。
ToStringメソッドには、さまざまな書式指定を引数で行えるが、多すぎるのでここでは割愛する。


指定した書式で文字列を整形するには、String.Formatメソッドを使用する。

var novelist = "やまだ";
var bestWork = "春麗";
var bookline = String.Format("Novelist={0};BestWork={1}", novelist, bestWork);

【技術書メモ】C#プログラミングのイディオム / 定石&パターン④

本のタイトルにもなっているイディオム
そもそもイディオムって何だろうと思っていたが、これはコーディングレベルでよく利用される汎用的なコードのこと。
なので、このイディオムを覚えれば、品質もよく、正しいコードを書けることに繋がりそう。特に自分が書いているコードって、思い通りには動くけど、プログラム上級者の人から見たら、どう思われるかいつも心配なので。。

初期化に関するイディオム

変数の宣言と初期化は同時に行うこと。

var age = 25;

配列とリストの初期化

初期化構文を使おう。初期化構文を使うと、要素の入れ替えや追加が楽になるから。

// 配列
var langs = new string[] { "C#", "VB", "C++", };
// リスト
var nums = new List<int> { 10, 20, 30, 40, };

Dictionaryの初期化

こちらも同じく初期化構文を使おう。

// Dictionary
var dict = new Dictionary<string, string>() {
    {"ja", "日本語" },
    {"en", "英語" },
    {"es", "スペイン語" },
    {"de", "ドイツ語" },
};

オブジェクトの初期化

こちらも同じく初期化構文を使用する。オブジェクト生成とプロパティの初期化が同時に行える。

var person = new Person {
    Name = "山田太郎",
    Birthday = new DateTime(1995, 11, 23),
};

初期化構文の特徴として、「要素の入れ替えや追加が容易になる」、「可読性が高まる」といった特徴がある。

判定のイディオム

単純な比較。

// 年齢が10才以下かという人の思考に基づく考え方
if (age <= 10)

数値が範囲内にあるかどうか。

// 数値を直線上に並べた比較
if(MinValue <= num && num <= MaxValue) 

if文の中でのreturn文の使い方。
このように記載すれば、可読性が高まる。

// ふるいにかけて残ったものだけ処理する
if (filePath == null)
    return;
if (GetOption() == Option.Skip)
    return;
// 実行したい処理

繰り返しのイディオム

ループ処理の考え方は、LINQ > foreach > forの順番で適用を考える。
なお、Listクラスの場合だけ、ForEachメソッドを使うと、foreach文よりも簡潔に繰り返し処理が記載できる。

var nums = new List<int> { 1, 2, 3, 4, 5, };
// 12345と出力される
nums.ForEach(n => Console.Write("{0}", n));

条件演算子、null合体演算子によるイディオム

条件演算子

// trueなら1、falseなら0を返却
var num = list.Contains(key) ? 1 : 0;

null合体演算子

// GetMessageがNULLを返却したら、DefaultMessageの値を変数messageにつめる
var message = GetMessage(code) ?? DefaultMessage();

null条件演算子

// sale変数がnullのとき、Productプロパティにアクセスせずにnullを返却する。
// NullPointerExceptionが発生しない
return sale?.Product;

プロパティに関するイディオム

まずは、プロパティの初期化。コンストラクタに初期化処理を書かなくていい。
Javaで慣れている分、プロパティにまだまだ慣れないが。。

// プロパティの初期化
public int MinimumLength { get; set; } = 6;

次が、値を変更できない読み取り専用のプロパティの定義方法。
こう定義すると、外のクラスから値の変更はできなくなる。

public int MinimumLength { get; private set; }

メソッドに関するイディオム

可変長引数。引数の数を限定したくない場合に使用する。
Javaでいうオーバーロードするところを、引数の数の違いだけなら、この書き方で吸収できるというもの。

// 定義
public void WriteInt(params int[] args) {

    foreach(var db in args) {

        Console.WriteLine(db);

    }
}

// 実際の呼び出し方
pp.WriteInt(1, 2, 3, 4, 5);

次にオプション引数。引数を省略した際の初期値を、それぞれ設定できるというもの。

// 定義
public void DoSomething(int num, string message = "失敗しました", int retryCount = 3) {

    Console.WriteLine("{0},{1},{2}",num, message, retryCount);

}

// 実際の呼び出し方
pp.DoSomething(100);
pp.DoSomething(100, "エラーです");
pp.DoSomething(100, "エラーです", 5);

その他のイディオム

ファイルパスの指定には、先頭に@を付加した逐次的リテラル文字列を使用する。
そうすると、¥記号がエスケープ文字として認識されなく、ファイルパスがそのまま記述可能。

var path = @"C:¥Example¥Greeting.txt";

2つの要素を入れ替える。

var temp = a;
a = b;
b = temp;

文字列を数値に変換する。
TryParseメソッドを使用する。エラーハンドリングも行ってくれて、便利!

int height;
if (int.TryParse(str, out height)) {
    // 変換に成功したときの処理
} else {
    // 変換に失敗したときの処理
}

MacBook Pro 13-inch, Mid 2010のメモリを16GBに増設した

最近はWindowsをメインマシンとしてきたが、Windowsが謎のフリーズを繰り返すようになり、仕方なく昔のMacを引っ張り出してきた。それが、MacBook Pro 13-inch, Mid2010だ。もう10年近く前に買ったことになる。
で、Macは起動はするのだが、何をするにも遅い。そこで、メモリを増設することにした。で、何も考えずに16GBに増設しようと考え、以下のメモリを購入する。

シリコンパワー ノートPC用メモリ DDR3 1600 PC3-12800 8GB×2枚 204Pin Mac 対応 永久保証 SP016GBSTU160N22

シリコンパワー ノートPC用メモリ DDR3 1600 PC3-12800 8GB×2枚 204Pin Mac 対応 永久保証 SP016GBSTU160N22

 

 で、交換作業自体はうまくいったが、何とMacが起動しない。いろいろ調べてみると、そもそもMacBook Pro 13-inch Mid 2010は16GBに公式には対応してないとのこと。
また、メモリは相性がいろいろあるらしく、結果自分が買ったメモリはうちのMacには合わなかったということらしい。。

そこで、もう一度調べていくと、MacBook Pro 13-inch Mid 2010と動確が取れてるメモリはいくつかあるが、Amazonから買えるメモリは以下のものだった。

 さっそく交換して、起動してみると起動したー!!
がしかし、MacBook Proの内蔵キーボードが反応しない。USB端子で接続している外部キーボードは反応するという謎事象。。
で、試して成功したのが、PRAMリセット。ようやく、メモリ交換が成功しました。

MacBook Pro 13-inch, Mid 2010で、メモリを16GBに増設することを検討している方は、この記事が参考になれば!

【技術書メモ】C#プログラミングのイディオム / 定石&パターン③

ラムダ式

ラムダ式は一種のメソッドで、メソッドをかなり簡略化することができる。

public void Do() {

    var numbers = new[] { 5, 3, 9, 6, 7, 5, 8, 1, 0, 5, 10, 4 };

    // 変数judgeに処理内容を代入
    Predicate<int> judge = (int n) => {
        if (n % 2 == 0) {
            return true;
         } else {
             return false;
         }
     };

     // Countメソッドの呼び出し
     var count = Count(numbers, judge);
}

// 第2引数のjudgeには、int型を受け取り、bool値を返すメソッドを指定可
public int Count(int[] numbers, Predicate<int> judge) {
    int count = 0;
    foreach(var n in numbers) {
        if(judge(n) == true) {
            count++;
        }
    }

    return count;
}

上記のようなソースがあったとして、変数judgeに代入している処理は、ラムダ式でだいぶ簡略化できる。

  • returnの右側には、式を書ける。
  • ラムダ式の{}が1つであれば、{}とreturnを省略可。
  • 引数の型を明示しなくてもよい。
  • 引数が1つであれば、()を省略可。

省略したソースがこちら。

var count = Count(numbers, n => n % 2 == 0);

とかなり省略できるが、ラムダ式は全然慣れてないので、初見だと何をやっているかわからない。また、Predicateの理解も曖昧だし、メソッドの引数にメソッドを指定するって考え方もピンとこない。慣れたら、便利なのかな。

Listクラスとラムダ式の組み合わせ

Listクラスには、ラムダ式を引数に受け取れるメソッドがたくさんあるらしい。
まずは、以下Listがあるとする。

var list = new List<string> {
    "Tokyo", "New Delhi", "Bankok", "london", "Paris", "Berlin", "Canbera", "Hong Kong"
};

このListに対して、ラムダ式を使ったメソッド呼び出し例を記載していく。
Existsメソッド

// 引数で指定した条件に一致する要素がするかをtrue / falseで返却
// 最初の文字が'A'である要素がリスト内に存在するかチェック
var exists = list.Exists(s => s[0] == 'A');

Findメソッド

// 引数で指定した条件と一致する要素を検索し、最初に見つかった要素を返却
// 文字列の長さが6文字の最初の要素を返却する(Bankok)
var name = list.Find(s => s.Length == 6);

FindIndexメソッド

// 引数で指定した条件と一致する要素を検索し、インデックスを返却
// 要素の中からBerlinを検索し、そのインデックスを返却
int index = list.FindIndex(s => s == "Berlin");
Console.WriteLine(index);

FindAllメソッド

// 引数で指定した条件と一致する、すべての要素を取得する
// Tokyo, Parisを取得(文字列が5文字以下)
var names = list.FindAll(s => s.Length <= 5);
foreach (var s in names) {
    Console.WriteLine(s);
}

RemoveAllメソッド

// 引数で指定した条件と一致すよ要素をリストから削除し、戻り値は削除した要素数
// London, Hong Kongを削除
var removedCount = list.RemoveAll(s => s.Contains("on"));
Console.WriteLine(removedCount);

ForEachメソッド

// リストの各要素に対して引数で指定した処理を実行
list.ForEach(s => Console.WriteLine(s));

// 上と下は同じこと
foreach(var s in list) {
    Console.WriteLine(s);
}

ConvertAllメソッド

// リスト内の要素を別の型に変換し、変換された要素が格納されたリストを返却
var lowerList = list.ConvertAll(s => s.ToLower());
lowerList.ForEach(s => Console.WriteLine(s));

LINQ to Objectsの基礎

まず、LINQの概要としては、オブジェクト、データベース、XMLなどのさまざまなデータに対して標準化された方法で問い合わせできるというもの。自分の中では、ソースに対して実施できるSQLのイメージできる。(selectやwhereなどがあるから)
また、クエリ演算子はIEnumerableに対する拡張メソッドとして定義されている。
ここでクエリ演算子の例を一つ。

// Whereで5文字以下の要素に絞り、Selectで、引数にラムダ式で小文字変換を指定し、その結果を出力
IEnumerable<string> query = list.Where(s => s.Length <= 5).Select(s => s.ToLower());

※シーケンスとは
標準クエリ演算子の捜査対象のデータのこと。(配列やListなど)
IEnumerableインターフェイスを実装するオブジェクトはすべてシーケンスとして見なされ、LINQのクエリ演算子を使用し、さまざまな操作が可能になる。

遅延実行と即時実行

遅延実行とは、本当にデータが必要になったときにクエリが実行されて、LINQの大きな特徴の一つ。

var list = new List<string> {
    "Tokyo", "New Delhi", "Bankok", "london", "Paris", "Berlin", "Canbera", "Hong Kong"
};

// ここで変数queryに値を代入しているようにみえるが、実際は検索結果が代入されていない
var query = list.Where(s => s.Length <= 5);

list[0] = "Osaka";

// 値が実際に必要になったタイミングで、クエリ演算子が実行される。
// そのため、実行結果は"Osaka","Paris"
foreach(var i in query) {
    Console.WriteLine(i);
}

即時実行とは、クエリを明示的に実行させることをいう。ここでは、ToArrayメソッドを使用した例を示す。

var list = new List<string> {
    "Tokyo", "New Delhi", "Bankok", "london", "Paris", "Berlin", "Canbera", "Hong Kong"
};

// ここで、Whereメソッド実行
var query = list.Where(s => s.Length <= 5).ToArray();

list[0] = "Osaka";

// すでにWhereメソッドが実行されているため、実行結果は"Tokyo","Paris"
foreach(var i in query) {
    Console.WriteLine(i);
}

【技術書メモ】C#プログラミングのイディオム / 定石&パターン②

C#についてがっつり勉強しようと思い、購入した技術書。
まだまだ序盤だが、評判通りのかなりの良書の予感。
uuc1h.hatenablog.jp
では、前回同様、まとめていきたいといきます。

複数の値の代入方法

このやり方は知らなかったので、メモ。

for (int feet = 1; feet <= 10; feet++) {
    double meter = feet * 0.3048;
    // feetが{0}に、meterが{1}に代入される
    // {0.0000}は、meterの値を小数点第4位まで表示する書式設定
    Console.WriteLine("{0}ft = {1:0.0000}m", feet, meter);
}

定数について

定数の定義方法は、以下の通り。

// constは、staticと解釈されるので、staticをつける必要なし
private const double ratio = 0.3048;

けど、constした定数は、publicにしない方がいいとある。
「けど、他クラスから定数を呼び出す場合は結構あるし、そもそもなんでpublicにするべきなのか?」と思っていると、バージョン管理問題に関連するらしい。
※ここで、バージョン管理問題の詳細は割愛

publicにして、他クラスからアクセスさせたい場合は、static readonlyを使えばいい。

public static readonly double Ratio = 0.3048;

オブジェクト初期化子

オブジェクト生成をする際、各フィールドの初期値を設定する。

Sale sale = new Sale {
    ShopName = "ぱんや";
    ProductCategory = "食品";
    Amount = 120;
}

Dictionary型をforeachで扱う際

KeyValuePair型を1つずつ取り出す。ここらへん、あんまりピンとこなかったが、ソースで表すとしっくりきた。

Dictionary<string, int> amountPerStore = sales.GetPerStoreSales();

foreach(KeyValuePair<string, int> obj in amountPerStore) {
    Console.WriteLine("{0}{1}", obj.Key, obj.Value);
}

KeyValuePair型は、キーと値がペアになっていて、上記のようにそれぞれ取り出せる。

C#インターフェイスについて

public class List<T> : IList<T>, ICollection<T>,,,

上記のように記載されていたら、Listクラスは、IList、ICollectionなどのインターフェイスに定義しているメソッドやプロパティを実装していることを示している。
そのため、

List<int> list = new List<int>() {1, 2, 3,4, 5};
ICollection<int> collection = list;
var count = collection.Count;

IEnumerable<int> enumerable = list;
  • Listクラスは、ICollection型の変数に代入でき、ICollectionインターフェイスが定義するプロパティ、メソッドが使える。
  • プロパティ、メソッドの具体的な動作は、Listクラスに実装されている。

といった点が特徴である。

メソッドの戻り値や引数にインターフェイスを指定しておくと、プログラム修正に強くなる。
例:引数がこれまでListだったメソッドを、やはり配列も受け取りたいとした場合、引数をIEnumerableにしておけば、どちらでも受け取れる。

varによる暗黙の型指定

メソッド内のローカル変数宣言の際、型名の代わりにvarを使うと、コンパイラが自動で型を判断してくれる。

var dict = new  Dictionary<string, int>();

なお、代入の右側から型が明らか出ない場合は、varを使用しないこと。

今日は、こんな感じで終わります。
こうやって、まとめると頭に入るけど、やっぱ時間かかるな。

【Unity】Unity2019.1.5f1でAndroid Buildをしようとしたら、「gradle build failed」と出てしまったときの対処方法

複合的な問題でした。
まずUnity2019では、Android SDK & NDK Toolsを一緒にインストールしている。そのため、Preferencesより、インストールしたファイルのパス設定を行った。自分が確認したときは、ズレていた。
詳しくは、以下参照。
https://framesynthesis.jp/tech/unity/android/

上記を直しても解決しない。。
ここで致命的だったのが、プロジェクト名に日本語を入れていたこと。。「タップRPG」にしていた。
プロジェクトを作り直し、そのときのプロジェクト名を「TapRPG」にしたら無事ビルドできましたー。

【NBA】私たちはまた3ピートの瞬間に立ち会えなかった。

まさか、ウォリアーズが負けるとは微塵も思ってなかったから、Game6が終わった直後はちょっと信じられなかった。
KDが怪我で出られないという強烈なアクシデントは合ったものの、KD抜きのウォリアーズも十二分に強い。最初の優勝は、KDいなかったし、シーズン73勝したときもKDはいなかった。そう、スプラッシュブラザーズに、グリーン、イグダーラがいれば、ウォリアーズの強さを発揮できる。

だが、トンプソンの怪我は致命的すぎた。

 特にこのGame6では絶好調だっただけに、トンプソンが怪我さえしなければGame 7があったのでは?と考えずにいられない。
しかもトンプソンは、左前十字靭帯断裂という大怪我だ。この怪我をきいて、まず思い出すのはデリック・ローズ。彼も同じ怪我をおい、大きくキャリアが狂ってしまった。
ローズとトンプソンではタイプが違う選手だが、カットインも多様するトンプソンだけに、万全な形で復帰できるのかが気になってしまう。

しかし、3ピートがなかなか見れない。以前はマイアミ・ヒートが達成しそうだったが、この時はスパーズに阻まれた。いや、正確にいうと当時のファイナルで覚醒したレナードに阻まれた。この時のレナードは、まだまだスター選手ではなかったが、ファイナルに突入するとまさに覚醒状態に入り、ヒートを、レブロンの夢を打ち砕いた。
今度は史上最強チームとの呼び声もあるウォリアーズの3ピートを、またもやレナードがストップしてしまった。

3ピートって、ホント難しいんすね。俺が生きているうちに、3ピートを達成するチーム出てくるかな。