存在しないキーの値をインクリメントすると

こんばんは:whale2:
各言語で`辞書[キー]+=1`のような処理を書くサンプルと、その処理で「そのようなキーはありません」といった旨のメッセージが出る言語についてどうすればよいのか。(伝わりづらい)

## 環境
実行環境は以下のとおりです。

```bash:環境
# コンピュータ
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.12.1
BuildVersion:	16B2555

# Ruby
$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin16]

# Perl
$ perl -v | grep v5
This is perl 5, version 24, subversion 0 (v5.24.0) built for darwin-thread-multi-2level

# Perl6
$ perl6 -v
This is Rakudo version 2016.07.1 built on MoarVM version 2016.07
implementing Perl 6.c.

# Python
$ python --version
Python 3.5.1 :: Anaconda 4.1.0 (x86_64)

# PHP5
$ php -v
PHP 5.6.25 (cli) (built: Sep  6 2016 16:37:16)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies
```


## Ruby
##### エラーになるパターン
```Ruby:エラー
target = %w[a a b b c 0 1 1 1]
count = {}

target.each{|var|
  count[ var ] += 1
}

puts count
=begin
#=> エラー
inc.rb:5:in `block in <main>': undefined method `+' for nil:NilClass (NoMethodError)
	from inc.rb:4:in `each'
	from inc.rb:4:in `<main>'
=end
```

##### 回避策
Rubyきれい。

```Ruby:inc.rb
target = %w[a a b b c 0 1 1 1]
count = Hash.new(0) # 初期値を設定

target.each{|var|
  count[ var ] += 1
}

puts count #=> {"a"=>2, "b"=>2, "c"=>1, "0"=>1, "1"=>3}
```

```Ruby:inc2.rb
target = %w[a a b b c 0 1 1 1]
count = {}
count.default = 0 # 初期値を設定

target.each{|var|
  count[ var ] += 1
}

puts count #=> {"a"=>2, "b"=>2, "c"=>1, "0"=>1, "1"=>3}
```

## Python
##### エラーになるパターン
```Python:エラー
target = 'a a b b c 0 1 1 1'.split(' ')
count = {}

for var in target:
    count[ var ] += 1

print( count )
'''
#=> エラー
Traceback (most recent call last):
  File "inc.py", line 6, in <module>
    count[ var ] += 1
KeyError: 'a'
'''
```

##### 回避策1
Pythonはもっと良い方法がありそう。

```Python:inc.py
target = 'a a b b c 0 1 1 1'.split(' ')
count = {}

for var in target:
    count.setdefault( var, 0 )
    count[ var ] += 1

print( count ) #=> {'a': 2, '1': 3, '0': 1, 'c': 1, 'b': 2}
```

##### 回避策2
defaultdict() を用いる方法。
コメント欄で@mpywさんに教えていただきました。 

```Python::inc2.py
from collections import defaultdict

target = 'a a b b c 0 1 1 1'.split(' ')
count = defaultdict(int)

for var in target:
    count[ var ] += 1

print( count ) #=> defaultdict(<class 'int'>, {'b': 2, '1': 3, '0': 1, 'a': 2, 'c': 1})
```

##### 回避策3
例外処理を書く方法。どの言語にも適用できそう。素敵。

```py3:inc3.py
target = 'a a b b c 0 1 1 1'.split(' ')
count = {}

for var in target:
    try:
        count[ var ] += 1
    except KeyError:
        count[ var ] = 1

print( count )
```

## PHP
##### エラーになるパターン
エラーではないもののUndefined indexなNoticeメッセージが出る。
~~`error_reporting( -1 );`を書かなければ出ない~~

```PHP:Notice
<?php
error_reporting( -1 );

$target = explode(' ', 'a a b b c 0 1 1 1');
$count = [];

foreach( $target as $var ){
  $count[ $var ] += 1;
}

echo json_encode( $count ) . PHP_EOL ;
/*
#=>
Notice: Undefined index: a in /Users/yuhei/inc.php on line 8

Notice: Undefined index: b in /Users/yuhei/inc.php on line 8

Notice: Undefined index: c in /Users/yuhei/inc.php on line 8

Notice: Undefined offset: 0 in /Users/yuhei/inc.php on line 8

Notice: Undefined offset: 1 in /Users/yuhei/inc.php on line 8
{"a":2,"b":2,"c":1,"0":1,"1":3}
*/
```

##### 回避策1(非推奨)
エラーを抑止してはいけない(戒め)

```PHP:非推奨
<?php
error_reporting( -1 );

$target = explode(' ', 'a a b b c 0 1 1 1');
$count = [];

foreach( $target as $var ){
  @$count[ $var ] += 1; # エラー抑止
}

echo json_encode( $count ) . PHP_EOL ; #=> {"a":2,"b":2,"c":1,"0":1,"1":3}
```

##### 回避策2
キーがない場合1を代入。という条件分岐を追加。

```PHP:isset()
<?php
error_reporting( -1 );

$target = explode(' ', 'a a b b c 0 1 1 1');
$count = [];

foreach( $target as $var ){
  if( isset( $count[ $var ] ) ){
    $count[ $var ] += 1;
  }
  else{
    $count[ $var ] = 1;
  }
}

echo json_encode( $count ) . PHP_EOL ; #=> {"a":2,"b":2,"c":1,"0":1,"1":3}
```

##### 回避策3
この例題については組み込み関数 `array_count_values()` のが良いと思う。

```PHP:array_count_values()
<?php
error_reporting( -1 );

$target = explode(' ', 'a a b b c 0 1 1 1');

$count = array_count_values( $target );

echo json_encode( $count ) . PHP_EOL ; #=> {"a":2,"b":2,"c":1,"0":1,"1":3}
```

##### 備考
`array_count_values()` の中でもキーがなければ1を代入。キーがあればインクリメント。という処理が行われている様子。
( キーが数値か文字列であることもチェックしていて素敵滅法 )

```C:php-5.6.27/ext/standard/array.c
/* {{{ proto array array_count_values(array input)
   Return the value as key and the frequency of that value in input as value */
PHP_FUNCTION(array_count_values)
{
        zval    *input,                 /* Input array */
                        **entry,                /* An entry in the input array */
                        **tmp;
        HashTable *myht;
        HashPosition pos;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
                return;
        }

        /* Initialize return array */
        array_init(return_value);

        /* Go through input array and add values to the return array */
        myht = Z_ARRVAL_P(input);
        zend_hash_internal_pointer_reset_ex(myht, &pos);
        while (zend_hash_get_current_data_ex(myht, (void **)&entry, &pos) == SUCCESS) {
                if (Z_TYPE_PP(entry) == IS_LONG) {
                        if (zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), (void **)&tmp) == FAILURE) {
                                zval *data;
                                MAKE_STD_ZVAL(data);
                                ZVAL_LONG(data, 1);
                                zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL);
                        } else {
                                Z_LVAL_PP(tmp)++;
                        }
                } else if (Z_TYPE_PP(entry) == IS_STRING) {
                        if (zend_symtable_find(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, (void**)&tmp) == FAILURE) {
                                zval *data;
                                MAKE_STD_ZVAL(data);
                                ZVAL_LONG(data, 1);
                                zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL);
                        } else {
                                Z_LVAL_PP(tmp)++;
                        }
                } else {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only count STRING and INTEGER values!");
                }

                zend_hash_move_forward_ex(myht, &pos);
        }
}
/* }}} */
```

## Perl
エラーにならないPerl最高。

```Perl:inc.pl
use v5.24;
use warnings;
use Data::Dumper;

my @target = qw[a a b b c 0 1 1 1];
my %count;

for my $var ( @target ){
  $count{ $var } += 1;
}

print Dumper \%count;
__DATA__
#=>
$VAR1 = {
          'c' => 1,
          '1' => 3,
          'b' => 2,
          'a' => 2,
          '0' => 1
        };
```

## Perl6
エラーにならないPerl6最(ry

```Perl:inc.pl6
use v6;

my @target = <a a b b c 0 1 1 1>;
my %count;

for @target -> Str $var {
  %count{ $var } += 1;
}

%count.say; #=> {0 => 1, 1 => 3, a => 2, b => 2, c => 1}
```

## おわり
各言語の人たちから刺されそう:whale2:

## 参考と注釈

* [Perl 5 version 24.0 documentation > Language reference > perlsyn](http://perldoc.perl.org/perlsyn.html)
* [Perl 6 Documentation > class Hash](https://docs.perl6.org/type/Hash)
* [Ruby 2.3.0 リファレンスマニュアル > ライブラリ一覧 > 組み込みライブラリ > Hashクラス](https://docs.ruby-lang.org/ja/latest/class/Hash.html)
* [Ruby | ハッシュの各要素に対して いきなりインクリメント ( += ) する - Qiita](http://qiita.com/YumaInaura/items/01e06852d94b71c6780e)
* [Python 3.5.2 ドキュメント » Python 標準ライブラリ » 4. 組み込み型](http://docs.python.jp/3.5/library/stdtypes.html)
* [How can I pre-increment at an empty array index without throwing a notice? - stackoverflow](http://stackoverflow.com/questions/19554358/how-can-i-pre-increment-at-an-empty-array-index-without-throwing-a-notice)
* [エルビス演算子でスマートに変数を初期化 - Qiita](http://qiita.com/do7be/items/7a2605f027ef7b42858e#comment-1d27ace40a73f3da1ba2)
* [isset, empty, is_null の動作まとめ - Qiita](http://qiita.com/mpyw/items/51bf42fe1ad8a7e535aa)
* [PHP マニュアル > 関数リファレンス > 変数・データ型関連 > 配列 > 配列 関数 > array_count_values](http://php.net/manual/ja/function.array-count-values.php)
* [プログラマが持つべき心構え (The Zen of Python) - Qiita](http://qiita.com/IshitaTakeshi/items/e4145921c8dbf7ba57ef)