Unityでゲームを作って、Androidアプリとしてリリースしました!
超絶シンプルですが、今の自分ができることを詰め込みました。無料で広告なしなので、ぜひぜひ遊んでみてください。
普段めちゃめちゃよく使う、配列とList
けど、配列が使えないと困る部分はあるので、一緒にまとめていく。
あまり使用頻度が少なそうだが、同じ値の要素をもったリスト、配列の作り方。
// -1が20個あるリスト var numbers = Enumerable.Repeat(-1, 20).ToList(); // -1が20個ある配列 var numbers = Enumerable.Repeat(-1, 20).ToArray();
ちなみに、-1を20個要素にもったリストを作ろうとすると、以下のように書く。
うん、これはだるい。
var numbers = new List<int> {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, };
ここからのやつは、かなり出番がありそう。
// 1から20まで連続した値をもったリスト var list = Enumerable.Range(1, 20).ToList(); // 1から20まで連続した値をもった配列 var array = Enumerable.Range(1, 20).ToArray();
こっから、よく使うやつたちです。
var numbers = new List<int> {1, 2, 3, 4, 5, }; var average = numbers.Average();
var sum = numbers.Sum();
var min = numbers.Min(); var max = numbers.Max();
var count = numbers.Count(n => n == 0);
bool exists = numbers.Any(n => n % 7 == 0);
bool isAllPositive = numbers.All(n => n > 0);
bool equal = numbers1.SequenceEqual(numbers2);
var text = "The quick brown fox jumps over the lazy dog"; var words = text.Split(' '); // 文字列overが取り出される var word = words.FirstOrDefault(x => x.Length == 4); // 文字列lazyが取り出される var word = words.LastOrDefault(x => x.Length == 4);
var numbers = new List<int> { 9, 7, -5, -4, 2, 5, 4, 0, -4, }; // 戻り値は2 var firstIndex = numbers.FindIndex(n => n < 0); // 戻り値は8 var lastIndex = numbers.FindLastIndex(n => n < 0);
これはちょっとつまづいたので、説明を入れる。TakeWhileメソッドを使えば、引数に与えた条件を満たしている間の要素を取り出せる。しかし、先頭から順番に条件に当てはまるか調べるが、先頭がすでに条件から外れていたら、その時点で処理をストップする。だから、以下コードのようになる。
var numbers = new List<int> { 9, 7, -5, -4, 2, 5, 4, 0, -4, }; // 9, 7を取り出す var selected = numbers.TakeWhile(x => x > 0); // 何も取り出さない var selected = numbers.TakeWhile(x => x < 0);
今度は、条件を満たしている間は要素を読み飛ばす。ただ、先頭が条件に合わないと処理がストップする動きは同じ。
var numbers = new List<int> { 9, 7, -5, -4, 2, 5, 4, 0, -4, }; // -5, -4, 2, 5, 4, 0, -4を取り出す var selected = numbers.SkipWhile(x => x > 0); // 9, 7, -5, -4, 2, 5, 4, 0, -4を取り出す var selected = numbers.SkipWhile(x => x < 0);
var numbers = new List<int> { 9, 7, -5, -4, 2, 5, 4, 0, -4, }; var result = numbers.Distinct();
この並び替えは、めちゃめちゃ使うと思う。かなり重要。
// 昇順にソート var numbers = new List<int> { 9, 7, -5, -4, 2, 5, 4, 0, -4, }; var selected = numbers.OrderBy(x => x).ToList(); selected.ForEach(Console.WriteLine); // 降順にソート var selected = numbers.OrderByDescending(x => x);
var numbers1 = new List<int> { 9, 7, -5, -4, 2, 5, 4, 0, -4, }; var numbers2 = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, }; var all = numbers1.Concat(numbers2).ToList(); all.ForEach(Console.WriteLine);
ここに書いたものが、すっと出てくるようになれば競プロもだいぶ解けるようになるんじゃないかと思う。反復あるのみですね。
配列とList
Atcoderなんかのような競技プログラミングをやっていると、必ず出てくるのが文字列の操作。文字列の中からある文字列を削除したり、くっつけたりと、そういった類の問題が出てくる印象がある。
で、ここらへんはすでにメソッドが用意されているので、知っていると知っていないでは、問題の解くスピードが変わってくる。けどね、全部を覚えるのは不可能です。じゃあ、どうしたらいいか考えた結果、メソッド自体を覚えるのではなく、「文字列の比較で、大文字と小文字の区別なく比較できるメソッドあったな」みたいに、こんなことができるメソッドがあったなということを覚えておけばいい感じだと思う。
で、これらを覚えておくには、ソースを書いて、そのメソッドを使ってみないとダメだ。ということで、以下つらつらと書いていきます。
var str1 = "ABC"; var str2 = "ABC"; if(str1 == str2) Console.WriteLine("OK");
一番シンプルなやつ。演算子が==なので、直感的でわかりやすい。
var str1 = "windows"; var str2 = "WINDOWS"; if(String.Compare(str1, str2, ignoreCase:true) == 0) { Console.WriteLine("等しい"); } else { Console.WriteLine("等しくない"); }
String.Compareを使えば、文字列比較の幅が一気に広がる。
これより以下、String.Compareを使った文字列比較の例。
var str1 = "カステラ"; var str2 = "かすてら"; // CultureInfoクラスは、言語、国、地域、暦などを表すクラス var cultureInfo = new CultureInfo("ja-JP"); if(String.Compare(str1, str2, cultureInfo, CompareOptions.IgnoreKanaType) == 0) { Console.WriteLine("等しい"); } else { Console.WriteLine("等しくない"); }
var str1 = "HTML5"; var str2 = "HTML5; // CultureInfoクラスは、言語、国、地域、暦などを表すクラス var cultureInfo = new CultureInfo("ja-JP"); if(String.Compare(str1, str2, cultureInfo, CompareOptions.IgnoreWidth) == 0) { Console.WriteLine("等しい"); } else { Console.WriteLine("等しくない"); }
ここまでString.Compareメソッドを使った、文字列比較をまとめてきた。次は、文字列の判定についてまとめてみる。ここで出てくる、「指定した部分文字列で始まっているか」などは、競プロでほんとよく出てくる。
var str = ""; if (String.IsNullOrEmpty(str)) Console.WriteLine("NG");
文字列のNULLと空文字チェックだが、自分が何も考えずに書くと以下のようになってしまう。
var str = ""; if (str == null || str == "") Console.WriteLine("NG");
こっちでも動くけど、IsNullOrEmptyメソッド使った方が分かりやすいし、確実だな。if文の条件は、バグポイントなので。
ちなみに、空白文字列まで調べられるメソッドもある。
if (String.IsNullOrWhiteSpace(str))
var str = "Visual Studio"; if (str.StartsWith("Visual")) Console.WriteLine("OK");
始まりがあれば終わりもあるということで、終わりを調べるメソッドもある。
if (str.EndsWith("Visual"))
始まりと終わりときて次は、そもそも場所関係なく部分文字列が含まれているかどうかを調べるときには、Containsメソッドを使う。
if (str.Contains("Program"))
Anyメソッドの引数の条件に合う文字があれば、trueを返す。
var target = "C# Programming";
var isExists = target.Any(c => Char.IsLower(c));
さきほどは、一つでも条件を満たした文字があるか調べるものだったが、今度はすべての文字が条件を満たしているか調べる。以下のコードだと、target変数の文字が、すべて数字だったら、trueを返す。
var target = "141421356";
var isAllDigits = target.All(c => Char.IsDigit(c));
これも、競プロでよく使うイメージがある。先頭を0から数えた、インデックスを返す。
var target = "C# Programming"; var index = target.IndexOf("Programming");
SubStringメソッドを使えば、開始位置と長さを指定することで、任意の文字列を取り出せる。
var target = "C# Programming"; // 文字列"Pr"を取り出す var value = target.Substring(3, 2);
ちなみに、Substringの第二引数に何も指定しなければ、文字列の最後まで取り出す。
var target = "C# Programming"; // 文字列"Programming"を取り出す var value = target.Substring(3);
残り、ちょっと一気に書く。
var target = " C# Programming ";
var replaced = target.Trim();
var target = "01234ABC567"; // 開始位置と長さを指定 var result = target.Remove(5, 3);
var target = "01234"; // 01abc234を返す var result = target.Insert(2, "abc");
var target = "I hope you"; var replaced = target.Replace("hope", "wish");
var replaced = target.ToUpper(); var replaced = target.ToLower();
var languages = new [] {"C#", "Java", "VB", "Ruby", }; var separator = ","; // 文字列"C", Java, VB, Ruby"を返す var result = String.Join(separator, languages);
var str = "C"プログラミング"; foreach (var c in str) Console.Write("{0}", c);
var chars = new char[] {'P', 'r', 'o', 'g', 'r', 'a', 'm' }; var str = new string(chars);
Javaでプログラムを書いていたころ、プロパティは以下のように書いていた。ゲッター、セッターだ。
private string name; public string SetName(string _name) { name = _name; } public string GetName() { return name; }
けど、C#ではこんな書き方ができる。
public string Name { get; set; } = 6;
この一行で、変数Nameの初期値は6で設定し、呼び出しもできる。ただ、外部から値をセットできると色々まずいので、読み取り専用プロパティの書き方がある。
// アクセスレベルがprivate public string GevenName { get; private set; } // getアクセサーのみ public string Name => FamilyName + " " + GivenName;
よし、プロパティ完璧に理解した。
よくif文使いがちだが、if文は簡単だし、分かりやすい反面、たしかに行数は多くなる。
var list = new List<int> { 1, 2, 3, 4, 5, }; var key = 2; int num; if (list.Contains(key)) { num = 1; else num = 2;
リストに2が含まれていたら変数numに1を、リストに2が含まれていなければ変数numに2を代入するコードを書くだけで、7行も必要になる。
けど、条件演算子を支えばもっと楽に書けるよというものである。
var nums = new List<int> { 1, 2, 3, 4, 5 }; var key = 2; var num = nums.Contains(key) ? 1 : 2;
条件演算子を使えば、3行で書けた。けど、慣れないとこの条件演算子の書式がかなりややこしい。。上の式でいうと、trueなら1、falseなら2を返している。
続いて、null合体演算子だ。
ヌルポを防ぐコードだと、一般的にはこう書くと思う。
var message = GetMessage(); if (message == null) message = "Default";
そう、if文で変数messageがnullかどうか、チェックしている。これも、null合体演算子を使えば楽に書ける。
var message = GetMessage() ?? "Default";
上のコードだと、GetMessage()がnullなら、??以降に書かれた戻り値を変数messageに代入する。
nullかどうか判断するコードで、null条件演算子というのもある。
これは、プロパティを持つオブジェクトのnullチェックで効力を発揮する。
return sale?.Product;
sale変数がnullの場合はnullを戻り値として返すが、sale変数がnullではない場合、sale.Productプロパティを返すというもの。
どれも、たしかに簡単に書けるけど、いざ書こうとすると多分if文で書いちゃうんだろうな。。条件演算子とnull合体演算子、完璧に理解した。
C#の特徴でもあるLINQ to Objects。自分の中では、C#でSQLの構文が使えるイメージでいる。それは、LINQに用意されているメソッドが、「Where」、「Select」などクエリ演算子と呼ぶからだ。
var list = new List<string> { "Tyokyo", "New Delhi", "Bangkok", "London", "Paris", "Berlin", "Canberra", "Honkong" }; // 文字列の長さが5以上の要素を抽出 IEnumerable<string> query = list.Where(s => s.Length <= 5);
このコードを見ると、List
List<string> names = list.FindAll(s => s.Length <= 5);
どちらも簡単な書式だし、ラムダ式を使っているし、わざわざLINGを使うメリットって何なのかというと、ここで使ったWhereメソッドはIEnuberable
// 配列でも使用できる var array = new string[] { "Tyokyo", "New Delhi", "Bangkok", "London", "Paris", "Berlin", "Canberra", "Honkong" }; IEnumerable<string> query = array.Where(s => s.Length <= 5);
反対に、FindAllクラスはList
var list = new string[] { "Tyokyo", "New Delhi", "Bangkok", "London", "Paris", "Berlin", "Canberra", "Honkong" }; // コンパイルエラーになる var names = list.FindAll(s => s.Length <= 5);
型によって、メソッドを変えるのが面倒なので、LINQを使った方が便利。また、Whereメソッドの戻り値がIEnumerable型なので、こんな書き方もできる。
var array = new string[] { "Tyokyo", "New Delhi", "Bangkok", "London", "Paris", "Berlin", "Canberra", "Honkong" }; // 元々はArray型だったものを、List型にキャストしている List<string> query = (List<string>)array.Where(s => s.Length <= 5);
インタフェースでやりとりできると、色々便利だ。
LINQのメリットはこれだけじゃないと思うけど、自分の中の理解としてはこんな感じ。
ということで、LINQ to Objects完璧に理解した。
何度も勉強しているが、すぐに忘れて使えなくなるのがラムダ式。ということで、何度も何度も勉強し直しています。
正直に言うと、「ラムダ式とは何ですか?」と聞かれてもうまく説明できる自身がない。自分の中の理解としては、ラムダ式はPredicateデリゲート型に変換できるため、Predicateデリゲート型を引数にもつ匿名メソッドに使用できる。
よって、引数に様々な条件を簡単な記述で渡せれるというのが、自分の中でのラムダ式というイメージ。
// Predicateデリゲート型変数matchに、ラムダ式を代入 Predicate<string> match = (string s) => { if (s[0] == 'A') return true; else return false; }; // 上で定義したPredicateデリゲート型変数matchを、Exixtsメソッドに代入 Console.WriteLine(list.Exists(match));
これが、Predicateデリゲート型を引数に持つメソッドに、ラムダ式を使った変数を渡しているコードだ。
けど、これじゃあ書き方が助長なので、簡素化したものがこちら。
Console.WriteLine(list.Exists(s => s[0] == 'A'));
さっきのコードと同じ結果が得られるから、こっちの書き方の方が簡単。けど、慣れないとこのコードが読めない。
自分は、=>の右側に条件を記載するものとして覚えた。=>の左側は、List
こんなざっくり理解でも、一応ラムダ式使えているのでOKとする。ラムダ式、完璧に理解した。