月別アーカイブ: 2022年11月

PHPプログラミングノート5(組み込み関数,ユーザー関数)

関数の作成法(JavaScriptとほぼ同じ感じ)

<?php

// 関数

// 引数なし 戻り値なし

function Hello_World(){

  echo'Hello World!';

}

Hello_World();

  echo '<br>';

// 引数あり 戻り値なし

function getString($string){

  echo$string;

}

getString('Hello_World!');

// 引数なし 戻り値なし
function getNumber(){
  return 5;
}

echo '<br>';

echo getNumber();

echo '<br>';

var_dump (getNumber());

$num = getNumber();

echo '<br>';

echo $num;

// 引数2つ 戻り値あり

function sumNumbers($num1, $num2){

  $num3 = $num1 + $num2;

  return $num3;
}
  echo '<br>';

  $total = sumNumbers(4,7);

  echo $total;

?>

↑を出力すると・・・

Hello World!
Hello_World!
5
int(5)
5
11

組み込み関数

<?php

// 組み込み関数 文字列の長さ

$strings = 'aiueo';

$strings1 = 'あいうえお';

echo strlen($strings);

echo '<br>';

// マルチバイト文字 日本語はSJIS, UTF-8 3~6バイトの場合

echo mb_strlen($strings1);

echo '<br>';

// 文字列の置換

$str = 'aiueo';

echo str_replace('aiueo','あいうえお',$str);

$split = 'Hello_World!';

//文字列の分割Rubyのsplitみたいなもの

echo '<pre>';

var_dump(explode('_', $split));

echo '</pre>';

// 正規表現で特定の文字が含まれるか確認する;

$str1 = 'Hello_World!';

echo preg_match('/Hello/',$str1);

echo '<br>';

// 指定文字列から文字列を取得する マルチバイト文字はmb_が必要

echo substr('Hello_World!', 5);

?>

↑を出力すると・・・

5
5
あいうえお

array(2) {
  [0]=>
  string(5) "Hello"
  [1]=>
  string(6) "World!"
}


1
_World!

ユーザー関数の作り方

Rubyと同じようにキャメルケースかスネークケースで名付ける

<?php

// ユーザー関数 郵便番号の書式になっているか調べる関数
//camelCase(ラクダ(camel)のコブのような書式)
$zipCode = '123-45678';
$zipCode1 = '123-4567';

function checkzipCode($str){

//変数内の-(ハイフン)を空白に置き換える
  $replaced = str_replace('-','',$str);

//文字列の長さを$lengthに代入する
  $length = strlen($replaced);

//文字列の長さを出力
  var_dump($length);

//文字列の長さが7のときにtrueを返すがそれ以外はfalseを返す
  if($length === 7){

    returntrue;

  }
    returnfalse;
  }

var_dump(checkzipCode($zipCode));

echo '<br>';

var_dump(checkzipCode($zipCode1));

?>

↑を出力すると・・・

int(8) bool(false)
int(7) bool(true)

8文字なのでfalseが帰り、7文字のときはTrueが帰ってくる

PHPプログラミングノート4(foreach~switch)

foreach

<?php

// foreach

$ary_persons = [

'name' => 'suzutuki',

'weight' => '55kg',

'height' => '179cm'

];

// Valueのみ表示

foreach($ary_persons as $ary_person){

  echo$ary_person;

}

// KeyとValue両方表示

  echo '<br>';

foreach($ary_persons as $key => $value){

  echo$key.'は'.$value.'です';

}

?>

↑を出力すると・・・

suzutuki55kg179cm
nameはsuzutukiですweightは55kgですheightは179cmです

入れ子が深い場合はこのように出力する

<?php

// foreach

// 連想配列

$ary_members_3 = [ 
'sun_group' => [ 
'suzutuki' => [ 
'weight' => '55kg',
 'height' => '179cm' 
], 
'akatuki' => [ 
'weight' => '70kg', 
'height' => '183cm'
 ],
 ], 
'moon_group' => [
 'akina' => [
 'weight' => '45kg',
 'height' => '149cm'
 ], 
'kotaro' => [
 'weight' => '60kg',
 'height' => '175m'
 ]
 ]
];

// Value両方表示

echo '<br>';

foreach($ary_members_3 as $ary_members_2){

foreach($ary_members_2 as $ary_members_1){

foreach($ary_members_1 as $ary_members => $value){

echo$ary_members.'は'.$value.'です';

// echo $key . 'は' . $value . 'です';

}

}

}

?>

↑を出力すると・・・

weightは55kgですheightは179cmですweightは70kgですheightは183cmですweightは45kgですheightは149cmですweightは60kgですheightは175mです

KeyとValue両方表示する場合は

<?php
// foreach

// 連想配列

$ary_members_3 = [ 
'sun_group' => [ 
'suzutuki' => [ 
'weight' => '55kg',
'height' => '179cm' 
], 
'akatuki' => [ 
'weight' => '70kg', 
'height' => '183cm'
],
], 
'moon_group' => [
'akina' => [
'weight' => '45kg',
'height' => '149cm'
], 
'kotaro' => [
'weight' => '60kg',
'height' => '175m'
]
]
];
// KeyとValue両方表示

echo '<br>';

foreach($ary_members_3 as $ary_members_2){

 foreach($ary_members_2 as $ary_members_1){
 
  foreach($ary_members_1 as $key => $value){

   echo $key . 'は' . $value . 'です';
}
}
}
?>

↑を出力すると・・・

weightは55kgですheightは179cmですweightは70kgですheightは183cmですweightは45kgですheightは149cmですweightは60kgですheightは175mです

for、while、do while文

<?php

// for、while文
for($i = 0; $i < 10; $i++ ) {

if($i === 4){
}
echo $i;
}

echo '<br>';

$j = 0;
while($j < 7){
echo $j;
$j++;
}
echo '<br>';

$h = 0;
do{
echo $h;
$h++;
}
while($h < 5);

?>

↑を出力すると・・・

0123456789
0123456
01234

switch文(Rubyのcase文に似ているが違う)

// switch文
$num = 1;
switch ($num) {

  case1:

    echo'1です';

    break;

  case2:

    echo'2です';

    break;
 
  case3:

    echo'3です';

    break;

  default:

    echo'1-3です';
}

↑を出力すると・・・

1です

PHPプログラミングノート3(演算子とif)

算術演算子の使い方

Rubyとほぼ同じだが、割り算をするときに小数点がつくのが違う部分だ

<?php

// 演算子

$num_1 = 9;

$num_2 = 6;

$result_1 = $num_1 + $num_2;

$result_2 = $num_2 - $num_1;

$result_3 = $num_1 * $num_2;

$result_4 = $num_2 / $num_1;

$result_5 = $num_1 % $num_2;

// 見やすくする

echo '<pre>';

echo $result_1;

echo '<pre>';

echo $result_2;

echo '<pre>';

echo $result_3;

echo '<pre>';

echo $result_4;

echo '<pre>';

echo $result_5;

echo '<pre>';

?>

↑を出力すると・・・

15
-3
54
0.66666666666667
3

if文の一番簡単な構文(Rubyとほぼ同じ)

<?php

// ifの構文

// if (条件) {

// 条件が真なら実行

// }

$point = 70;

if ($point >= 70) {

echo'テストの合格点は'.$point.'以上なので合格です';

  }

?>

↑を出力すると・・・

テストの合格点は70以上なので合格です

※また==と===があり、Rubyと同じで===は型も一致しているかどうか判定する

<?php

// ifの構文

// if (条件) {

// 条件が真なら実行

// }else{

// }

$point = 60;

if ($point >= 70) {

echo'テストの合格点は'.$point.'以上なので合格です';

}else{

echo'不合格です';

}

?>

↑を出力すると・・・

不合格です

2重if文や、else if、 elseなど

<?php

// ifの構文

// if (条件) {

// 条件が真なら実行

// }else{

// }

$point = 72;

$report = true;

if ($point >= 90) {

if ($report){

echo'評価はSです';

}

}else if($point >= 70){

echo'評価はAです';

}

else{

echo'不合格です';

}

?>

わざわざ2重if文を使うよりもRubyのように and && or || を使うと良い

!= はRubyと同じでNotEqualだが、!==は型も一致しているかどうかで調べられるのがRubyと違うところ

空かどうか?(is_null,empty)Rubyとほぼ同じ

$ary = [];

$report = true;

if (empty($ary) ) {

echo'空です';

}else{

echo'なにか入っています';

}

?>

3項演算子Rubyとほぼ同じ(ただし直接出力ができない)

<?php

// ifの構文

// 3項演算子の構文
// 条件 ? 真 : 偽

$ary = [];

$result = empty($ary) ? '空です' : 'なにか入っています';

echo $result;

?>

↑を出力すると・・・

空です

PHPプログラミングノート2(連想配列)

連想配列はよく使うので理解を深める

連想配列 (Rubyのハッシュににている)

<?php

// 連想配列

$ary_person = [

'name' => 'suzutuki',

'weight' => '55kg',

'height' => '179cm'

];

echo $ary_person['height'];

echo('<br>');

var_dump($ary_person);

?>

実行すると・・・

179cm
array(3) { ["name"]=> string(8) "suzutuki" ["weight"]=> string(4) "55kg" ["height"]=> string(5) "179cm" }

更に入れ子構造になっている連想配列

<?php

// 連想配列

$ary_member = [

  'sun_group' => [

  'suzutuki' => [

  'weight' => '55kg',
 
  'height' => '179cm'

],
  
  'akatuki' => [

  'weight' => '70kg',

  'height' => '183cm'

],

],

  'moon_group' => [

  'akina' => [

  'weight' => '45kg',

  'height' => '149cm'

],

  'kotaro' => [

  'weight' => '60kg',

  'height' => '175m'

]

]

];
// moon_groupのakinaのweightを出力
echo$ary_member['moon_group']['akina']['weight'];

// 見やすくする

echo'<pre>';

//ary_memberのすべてを出力する
var_dump($ary_member);

echo'<pre>';

?>

↑を出力すると・・・

45kg
array(2) {
  ["sun_group"]=>
  array(2) {
    ["suzutuki"]=>
    array(2) {
      ["weight"]=>
      string(4) "55kg"
      ["height"]=>
      string(5) "179cm"
    }
    ["akatuki"]=>
    array(2) {
      ["weight"]=>
      string(4) "70kg"
      ["height"]=>
      string(5) "183cm"
    }
  }
  ["moon_group"]=>
  array(2) {
    ["akina"]=>
    array(2) {
      ["weight"]=>
      string(4) "45kg"
      ["height"]=>
      string(5) "149cm"
    }
    ["kotaro"]=>
    array(2) {
      ["weight"]=>
      string(4) "60kg"
      ["height"]=>
      string(4) "175m"
    }
  }
}

PHPのまとめ1環境構築から2次元配列まで(プログラミングノート)

PHP学習のための準備

MAMPをインストールする

MAMPで検索してインストールする

MAMP→htdocs→php_testのファイルを作成して→PHPファイルを設置してブラウザで確認する

ブラウザで確認する方法

http://127.0.0.1/php_test/ファイル名

PHPの基本構文<?php?>

<?php
  echo('test');
  phpinfo();
?>

<?php から始まり ?> で終わる。実行するときは;(セミコロン)を利用する

HTMLとPHPを使うとき

<!DOCTYPE html>
<head></head>
<body>

こちらはHTMLだよ

<?php
  echo('こちらはPHPだよ');
?>
</body>
</html>

コメント数字や文字列の使い方

<?php
// 1行コメントのときは//

/*複数行コメントの場合は/**/の間に書く
*/

//半角のときは数値として認識される

 echo(789);
echo('<br>');
//全角の場合文字列として認識される

echo(789);
echo('<br>');
//文字列の場合はRubyと同じで''""を使う

echo('文字列');
echo('<br>');
echo("文字列");
echo('<br>');
?>

変数と.について

<?php

// 変数 Rubyと同じ動的型付け言語 先頭は英文字 × 1int ○_numはできる

$num = 123;

$num2 = 234;

$sum = $num . $num2;

$strings = '文字列だよ';

// 配列 オブジェクト コレクション型

var_dump($sum);

?>

実行結果

string(6) "123234"

数字同士を足したはずなのに文字列同士を足して出力された。

暗黙的に変換された?(Rubyとは違う)

定数 (constで定義) JavaScriptに似ている

<?php

// 定数 変わらないものに使う 文字

// constantの略

const TAX = '10%';

echo TAX;

?>

配列 Rubyとほぼ同じ感じで[]内に入れる

<?php

// 配列

$ary = [1,2,3];
$ary2 = ['aiueo',5,7];
echo $ary[1];
// 見やすくする
echo '<pre>';

var_dump($ary);
var_dump($ary2);
echo '</pre>';

?>

出力すると・・・

2
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
array(3) {
[0]=>
string(5) "aiueo"
[1]=>
int(5)
[2]=>
int(7)
}

2次元配列の作り方 (Rubyとほぼ同じ)

<?php

// 2次元配列

$ary3 = [

[4,5,6],

[8,9,10]

];

var_dump($ary3);

?>

↑を出力すると・・・

array(2) { [0]=> array(3) { [0]=> int(4) [1]=> int(5) [2]=> int(6) } [1]=> array(3) { [0]=> int(8) [1]=> int(9) [2]=> int(10) } }

PaizaAランクLvアップ問題と解説(Ruby)その10(累積和の計算)

※スキルチェック問題ではありません。
規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました。

1:累積和の計算 (paizaランク C 相当)

数列 A についての情報が与えられるので、 1 ~ N の各 i について、和 S_i = A_1 + … + A_i を求めてください。

入力される値

N       
A_1 A_2 ... A_N
・ 1 行目には、数列 A の要素数 N が与えられます。
・ 2 行目には、数列 A の各要素 A_1, A_2 … A_N が与えられます。入力値最終行の末尾に改行が1つ入ります。
文字列は標準入力から渡されます。
期待する出力
N 行の出力

・S_i(1 ≦ i ≦ N) を出力してください。

S_1     
...     
S_N
条件

すべてのテストケースにおいて、以下の条件をみたします。
・ 1 ≦ N ≦ 10 ^ 5
・ -100 ≦ A_i ≦ 100
入力例1

10
0 1 2 3 4 5 6 7 8 9
出力例1

0
1
3
6
10
15
21
28
36
45
入力例2

1
100
出力例2

100

解答と解説

1:総当り法(良くない方法パフォーマンスが良くない)

計算量がO(N²)となってしまい。めちゃくちゃ時間がかかってしまいます。

#1行目の入力値(数値)を変数に代入する
n = gets.to_i

#2行目の半角スペース刻みの入力値(数値)を変数に多重代入する
m = gets.split.map(&:to_i)

#n回繰り返す
n.times do |i|
 
#mの配列の0(初めから)からmまでの範囲を足したものを出力する
  puts m[0..i].sum 

end

計算量がO(N²)となってしまい。↑のようにテスト4でめちゃくちゃ時間がかかってしまいます。

2:別解    いい方法(パフォーマンスが良い)

1:変数xを予め用意して(変数名はわかりやすいものなら何でもOKです)0で初期化します
2:変数xに現在の値m[i]を加算して出力します。

#1行目の入力値(数値)を変数に代入する
n = gets.to_i
#2行目の半角スペース刻みの入力値(数値)を変数に多重代入する
m = gets.split.map(&:to_i)
x = 0

#n回繰り返す
n.times do |i|
#xに加算して現在の値m[i]を加算して出力する。
 puts x += m[i]
end

計算量がO(N)になったことでテスト4が劇的に速くなりました!

↑のコードを短くすると

n = gets.to_i
m = gets.split.map(&:to_i)
x = 0
n.times { |i| puts x += m[i] }

なぜ解答1は実行時間が遅いのか?(入力例1で解説)

解答1の場合このような毎回0から加算していく計算をするので計算数が増えてしまいます。

0                                                                                計算回数0回  0を出力

0+1=1                                計算回数1回  1を出力

0+1+2=3                           計算回数2回  3を出力

0+1+2+3=6                計算回数3回  6を出力

0+1+2+3+4=10             計算回数4回  10を出力

0+1+2+3+4+5=15           計算回数5回  15を出力

0+1+2+3+4+5+6=21         計算回数6回  21を出力

0+1+2+3+4+5+6+7=28       計算回数7回  28を出力

0+1+2+3+4+5+6+7+8=36     計算回数8回  36を出力

0+1+2+3+4+5+6+7+8+9=45   計算回数9回  45を出力

nが10付近になると膨大な計算数になってしまいます

なぜ解答2で実行時間が速くなるのか?

結論:変数xに加算していき、加算したあとの変数xを出力することで計算数を減らせたので速くなりました。

私達人間が一般的に頭の中で計算して累積和を出す計算方法を使う。

0       計算回数0回
0+1=1   計算回数1回
1+2=3   計算回数1回
3+3=6   計算回数1回
6+4=10  計算回数1回
10+5=15 計算回数1回
15+6=21 計算回数1回
21+7=28 計算回数1回
28+8=36 計算回数1回
36+9=45 計算回数1回

というように

これまでに足した数(例:45)と今計算したい数字(例:10)を加算して答えを出します。

解答2はこれまでに足した数x今計算したい数字m[i]を足して、加算したあとに出力しているので計算数を減らすことができたので実行時間を早くすることができました!

再帰関数で階乗を求める (Ruby)

目標

アルゴリズムを利用した方法で再帰関数がよく使われるので練習して使えるようにする

そもそも再帰関数とは?

アルゴリズムなどを記述するときに、自分自身を引用する形で定義することを再帰的定義といい
自分自身を呼び出す関数のことを再帰関数といいます。

再帰関数の基本的なルールは2つです。

1:自分自身の関数を用いて、問題を少し簡単にするための計算式をたてる
2:簡単なパターンの場合の答えを用意する

階乗を求める関数factorialを用いて問題を解いてみましょう。

階乗(factorial)を求めるメソッド

def factorial(n) 
  if n <= 0  
 #0! = 1なので1を返す
     return 1
  else
   # 動作確認用
   p n
   #ここでfactorial自身を呼び出す
   return n * factorial(n-1) 
  end
end
p factorial(5) #120

5! = 120なので問題ないですね。

p nで動きを確認すると・・・

5
4
3
2
1

↑のようにnの値が変化していることを確認できます。

p n * fact(n-1) で動きを確認すると・・・

1
2
6
24
120

↑のように出力されることが確認されます。
これは再帰関数を使わない、each,times,whileなどのようなループとは挙動が違い、
初めて使う場合はなぜこのようになるのか混乱してしまいます。

そこでpコマンドなどで処理の流れを見ることでどうなっているのか確認します。

p “#{n} * #{fact(n-1)}” で処理を確認してみると・・・

"1 * 1"
"2 * 1 * 1"
"3 * 2 * 1 * 1"
"4 * 3 * 2 * 1 * 1"
"5 * 4 * 3 * 2 * 1 * 1"
"5 * 4 * 3 * 2 * 1 * 1"

↑のように計算していることがわかりますね。

実際はこのように動いているようです。

1:factorial(5)はfactorial(4) × 5 を返すことになっているので、関数factorial(4)を呼び出します。
2:factorial(4)はfactorial(3) × 4 を返すことになっているので、関数factorial(3)を呼び出します。
3:factorial(3)はfactorial(2) × 3 を返すことになっているので、関数factorial(2)を呼び出します。
4:factorial(2)はfactorial(1) × 2 を返すことになっているので、関数factorial(1)を呼び出します。
5: factorial(1)はfactorial(0) × 1 を返すことになっているので、関数factorial(0)を呼び出します。
6:factorial(0)はN<=0の条件をみたすので1を返します。
7:factorial(1)はfactorial(0)の呼び出しが終わったので1 × 1 = 1を返します。
6:factorial(2)はfactorial(1)の呼び出しが終わったので2 × 1 × 1 = 2を返します。
7:factorial(3)はfactorial(1)の呼び出しが終わったので3 × 2 × 1 × 1= 6を返します。
8:factorial(4)はfactorial(1)の呼び出しが終わったので4 × 3 × 2 × 1 × 1 = 24を返します。
9:factorial(5)はfactorial(1)の呼び出しが終わったので5 × 4 × 3 × 2 × 1 × 1 = 120を返します

これにより5~9の部分が出力されたので1,2,6,24,120というように出力されました。

PaizaAランクLvアップ問題と解説(Ruby)その6

3から続きます

※スキルチェック問題ではありません。
規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました。

マップの判定・縦横

1:移動が可能かの判定・方角 (paizaランク B 相当)

マップの行数 H と列数 W , 障害物を ‘#’ で、移動可能な場所を ‘.’ で表した H 行 W 列のマップ S_1 … S_H が与えられます。
続けて現在の座標 sy , sx ,1マス移動する方角 m が与えられるので、移動が可能かどうかを判定してください。

移動が可能であるということは、以下の図の通り
「移動先が障害物でない かつ 移動先がマップの範囲外でない」
ということを意味します。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。


入力される値

H W sy sx m     
S_0    
...     
S_(H-1)
・ 1 行目にはマップの行数を表す整数 H , マップの列数を表す整数 W , 現在の y, x 座標を表す sy sx , 1 マス移動する方角 m が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、マップの i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、マップの i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)入力値最終行の末尾に改行が1つ入ります。
期待する出力
移動が可能である場合 “Yes” を、不可能である場合 “No” を出力してください。

Yes

または

No

条件

すべてのテストケースにおいて、以下の条件をみたします。

・ 1 ≦ H, W ≦ 20
・ 0 ≦ sy < H , 0 ≦ sx < W
・ S_i は W 文字の文字列
・ マップ上の(sy, sx)のマスは必ず '.'
・ S の各文字は '.' または '#'
・ m は、N, S, E, W のいずれかであり、それぞれ 北・南・東・西 を意味します。
入力例1

3 3 1 1 E
..#
..#
...
出力例1

No
入力例2

9 2 4 0 S
#.
#.
..
##
..
..
.#
..
.#
出力例2

Yes

解答と解説(入力例1で行います)

#方角のみ文字列なので、それ以外を数値として変数に代入して、mに文字列として代入する
h,w,y,x,m = gets.split.map.with_index { |val, i| i == 4 ? val : val.to_i }

#盤面の情報をバラバラにして代入する 結果→[[".", ".", "#"], [".", ".", "#"], [".", ".", "."]]
chart = h.times.map { gets.chomp.chars}

#移動操作した時の座標の移動パターンを作成する。
direction = { N: [-1, 0], S: [1, 0], E: [0, 1], W: [0, -1] } 
#入力例1の場合mがEなので[0, 1]yとxに加算する
y += direction[m.to_sym][0]
x += direction[m.to_sym][1]

#画面外だとアウトなのでyとxが0以上でyがh未満xがw未満であることを記述する
#移動後の座標の現在地(chart[y][x])が"."であることを満たしたときに"Yes"
  if y >= 0 && x >= 0 && y < h && x < w && chart[y][x] == "."
    puts "Yes"
  それ以外を"No"と出力する
  else puts "No"
end

2:移動が可能かの判定・方向 (paizaランク B 相当)

マップの行数 H と列数 W , 障害物を ‘#’ , 移動可能な場所を ‘.’ で表した H 行 W 列のマップ S_1 … S_H が与えられます。
続けて現在の座標 sy , sx , 現在向いている方角 d , 1マス移動する方向 m が与えられるので、移動が可能かどうかを判定してください。

移動が可能であるということは、以下の図の通り
「移動先が障害物でない かつ 移動先がマップの範囲外でない」
ということを意味します。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。


入力される値

H W sy sx d m      
S_0     
...     
S_(H-1)
・ 1 行目にはマップの行数を表す整数 H , マップの列数を表す整数 W , 現在の y, x 座標を表す sy sx , 現在向いている方角 d , 1 マス移動する方向 m が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、マップの i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、マップの i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)
入力値最終行の末尾に改行が1つ入ります。
期待する出力
移動が可能である場合 “Yes” を、不可能である場合 “No” を出力してください。

Yes

または

No
条件

すべてのテストケースにおいて、以下の条件をみたします。

・ 1 ≦ H, W ≦ 20
・ 0 ≦ sy < H , 0 ≦ sx < W
・ S_i は W 文字の文字列
・ マップ上の(sy, sx)のマスは必ず '.'
・ S の各文字は '.' または '#'
・ d は、N, S, E, W のいずれかであり、それぞれ 北・南・東・西 を意味します。
・ m は、L, R のいずれかであり、それぞれ 左・右 を意味します。
入力例1

2 6 0 4 E L
####..
##..#.
出力例1

No
入力例2

7 9 6 0 S R
..#.#..##
..#..#...
#.......#
#.#...###
#.##....#
.....#...
..##..#.#
出力例2

No

解答と解説(入力例1で行います)

#dとmが文字列なので、それ以外を数値として変数に代入して、dとmに文字列として代入する 
h,w,y,x,d,m = gets.split.map.with_index { |val, i| i >= 4 ? val : val.to_i }

#盤面の情報をバラバラにして代入する  結果→[["#", "#", "#", "#", ".", "."], ["#", "#", ".", ".", "#", "."]]
chart = h.times.map { gets.chomp.chars}

#Rが来たときの移動パターンを設定する
operateR = { N: [0, 1], S: [0, -1], E: [1, 0], W: [-1, 0] } 
#Lが来たときの移動パターンを設定する
operateL ={ N: [0, -1], S: [0, 1], E: [-1, 0], W: [1, 0] } 

#入力例1の場合mがLで向いている方向がEなので[-1, 0]yとxに加算する
if m == "R"
  y += operateR[d.to_sym][0] 
  x += operateR[d.to_sym][1] 
elsif m == "L"
  y += operateL[d.to_sym][0] 
  x += operateL[d.to_sym][1] 
end

#画面外だとアウトなのでyとxが0以上でyがh未満xがw未満であることを記述する
#移動後の座標の現在地(chart[y][x])が"."である条件を満たしたときに"Yes"
  if y >= 0 && x >= 0 && y < h && x < w && chart[y][x] == "."
    puts "Yes"
  else puts "No"
end

※移動後の座標が[-1,4]で画面外なので”No”が出力された

3:移動が可能かの判定・複数回の移動 (paizaランク B 相当)

マップの行数 H と列数 W , 現在の座標 sy , sx , 移動の回数 N が与えられます。
続けて、障害物を ‘#’ で、移動可能な場所を ‘.’ で表した H 行 W 列 のマップ S_1 … S_H と N 回の移動の向き d_1 … d_N が与えられます。

移動者ははじめ北を向いています。移動者は、1 回の移動で次の行動を行います。

「移動の向きに方向転換したのち、1 マス進む。」

各移動が可能である場合、移動後の y , x 座標を出力してください。
移動が可能でない場合、移動後の座標を出力する代わりに “Stop” を出力して、以降の移動を打ち切ってください。

各移動が可能であるということは、以下の図の通り
「移動先が障害物でない かつ 移動先がマップの範囲外でない」
ということを意味します。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。


右に移動

入力される値

H W sy sx N     
S_0     
...     
S_(H-1)     
d_1     
...     
d_N
・ 1 行目にはマップの行数を表す整数 H , マップの列数を表す整数 W , 現在の y, x 座標を表す sy sx , 移動する回数 N が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、マップの i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、マップの i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)
・ 続く N 行のうち i 行目 (1 ≦ i ≦ N) には、i 回目の移動の向き d_i が与えられます。
入力値最終行の末尾に改行が1つ入ります。
期待する出力
M (1 ≦ M ≦ N) 行の出力

・ k (1 ≦ k ≦ M) 回目の移動後の y , x 座標、y_k, x_k を出力してください。
・ ただし、M 回目で移動しきれない場合、”Stop” を出力してください。

y_1 x_1
...
y_k x_k
...
y_M x_M

または

y_1 x_1
...
y_k x_k
...
y_(M-1) x_(M-1)
Stop
条件

すべてのテストケースにおいて、以下の条件をみたします。

・ 1 ≦ H, W ≦ 20
・ 1 ≦ N ≦ 100
・ 0 ≦ sy < H , 0 ≦ sx < W
・ S_i は W 文字の文字列
・ マップ上の(sy, sx)のマスは必ず '.'
・ S_i の各文字は '.' または '#'
・ d_i は、L, R のいずれかであり、それぞれ 左・右 を意味します。
入力例1

7 3 2 1 5
..#
...
...
...
..#
.#.
##.
L
L
L
L
L
出力例1

2 0
3 0
3 1
2 1
2 0
入力例2

7 11 1 5 43
.##........
.#......##.
.#....#...#
.###......#
#......###.
..#....###.
#.#........
L
L
R
L
R
L
R
L
L
R
L
R
L
L
L
L
R
R
R
L
R
L
R
L
L
R
L
L
R
L
R
L
R
R
R
R
L
R
L
L
L
R
R
出力例2

1 4
2 4
2 3
Stop

解答と解説(入力例2で行います)

直感的でわかりやすいけどあまりよろしくないかも

#数値として各変数に代入する
h,w,y,x,n = gets.split.map(&:to_i)

#盤面の情報をバラバラにして代入する 実行 → [[".", "#", "#", ".", ".", ".", ".", ".", ".", ".", "."], [".", "#", ".", ".", ".", ".", ".", ".", "#", "#", "."], [".", "#", ".", ".", ".", ".", "#", ".", ".", ".", "#"], [".", "#", "#", "#", ".", ".", ".", ".", ".", ".", "#"], ["#", ".", ".", ".", ".", ".", ".", "#", "#", "#", "."], [".", ".", "#", ".", ".", ".", ".", "#", "#", "#", "."], ["#", ".", "#", ".", ".", ".", ".", ".", ".", ".", "."]]
chart = h.times.map { gets.chomp.chars}

# Rが来たときの移動パターンを設定する
operateR = { N: [0, 1], S: [0, -1], E: [1, 0], W: [-1, 0] } 

#Lが来たときの移動パターンを設定する
operateL ={ N: [0, -1], S: [0, 1], E: [-1, 0], W: [1, 0] } 

#最初は北を向いているのでdに”N”を代入する
d = "N"

#コマンドがn回あるのでn回繰り返す
n.times do
  #RかLの入力値を受け取り代入する
  command = gets.chomp

#自分が向いている向きdとコマンドL,Rによってyとxを加算する
  if command == "R"
     y += operateR[d.to_sym][0] 
     x += operateR[d.to_sym][1] 
  elsif command == "L"
     y += operateL[d.to_sym][0] 
     x += operateL[d.to_sym][1] 
  end

#自分の向いている向きとコマンドのLかRかで自分の向いている向きを変更する。
  if d == "N" && command == "L" || d == "S" && command == "R"
     d = "W"
  elsif d == "W" && command == "L" || d == "E" && command == "R"
     d = "S"
  elsif d == "E" && command == "L" || d == "W" && command == "R"
     d = "N"
  elsif d == "S" && command == "L" || d == "N" && command == "R"
     d = "E"
  end

#画面外だとアウトなのでyとxが0以上でyがh未満xがw未満であることを記述する
#移動後の座標の現在地(chart[y][x])が"."である条件を満たしたときに"Yes"
  if y >= 0 && x >= 0 && y < h && x < w && chart[y][x] == "."
     puts y.to_s + " " + x.to_s

条件を満たさなかったときに”Stop”を出力して繰り返しを終わらせる(break)
  else puts "Stop"
     break
  end
end

別解

#数値として各変数に代入する
h, w, y, x, n = gets.split.map(&:to_i)

#盤面の情報をバラバラにして代入する 実行 → [[".", "#", "#", ".", ".", ".", ".", ".", ".", ".", "."], [".", "#", ".", ".", ".", ".", ".", ".", "#", "#", "."], [".", "#", ".", ".", ".", ".", "#", ".", ".", ".", "#"], [".", "#", "#", "#", ".", ".", ".", ".", ".", ".", "#"], ["#", ".", ".", ".", ".", ".", ".", "#", "#", "#", "."], [".", ".", "#", ".", ".", ".", ".", "#", "#", "#", "."], ["#", ".", "#", ".", ".", ".", ".", ".", ".", ".", "."]]
chart = h.times.map { gets.chomp.chars }

#移動するパターンを作成する 北 東 南 西 の順番
direction = [[-1, 0], [0, 1], [1, 0], [0, -1]]

#状態を定義する。最初は0
current = 0

#コマンドがn回あるのでn回繰り返す
n.times do
 #RかLの入力値を受け取り代入する
  d = gets.chomp
 #Lの時−1してRのときに+1する
  d == "L" ? current -= 1 : current += 1
  y += direction[current % 4][0]
  x += direction[current % 4][1]

  if y >= 0 && x >= 0 && y < h && x < w && chart[y][x] == "."
    puts y.to_s + " " + x.to_s
# 条件を満たさなかったときに”Stop”を出力して繰り返しを終わらせる(break)
  else puts "Stop"
    break
  end
end

入力例2

1ループ目

最初の状態はcurrent = 0でコマンドがL、yとx が[1,5]
currentが  -1になり
-1 % 4 は 3なので
direction[3]は[0,-1]
yとxに [0,-1]を加算して
yとx が[1,4]

2ループ目

最初の状態はcurrent = -1でコマンドがL、yとx が[1,4]
currentが  -2になり
-2 % 4 は 2なので
direction[2]は[1,0]
yとxに [1,0]を加算して
yとx が[2,4]

3ループ目

最初の状態はcurrent = -2でコマンドがR、yとx が[2,4]
currentが  -1になり
-1 % 4 は 3なので
direction[3]は[0,-1]
yとxに [0,-1]を加算して
yとx が[2,3]

4ループ目

最初の状態はcurrent = -2でコマンドがL、yとx が[2,3]
currentが  -2になり
-2 % 4 は 2なので
direction[2]は[1,0]
yとxに [1,0]を加算して
yとx が[3,4]
移動先が”#”があるので”Stop”を出力してループを抜ける

※ポイント

d == ‘L’ ? current -= 1 : current += 1
でcurrentの値を変えるだけで移動操作が表現できる。

4:移動が可能かの判定・幅のある移動 (paizaランク B 相当)

マップの行数 H と列数 W , 障害物を ‘#’ で移動可能な場所を ‘.’ で表した H 行 W 列のマップ S_1 … S_H , 現在の座標 sy, sx, 移動の回数 N が与えられます。
続けて、 N 回の移動の向き d_1 … d_N と移動するマス数 l_1 … l_N が与えられます。

プレイヤーははじめ北を向いています。

各移動が可能である場合、移動後の y , x 座標 を出力してください。
移動が可能でない場合(移動しきれない場合)、移動できるところまで移動した後の座標を出力した後に “Stop” を出力して、以降の移動を打ち切ってください。

各移動が可能であるということは、以下の図の通り
「今いるマスから移動先のマスまでに障害物がない かつ 移動先がマップの範囲外でない」
ということを意味します。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。


入力される値

H W sy sx N        
S_0         
...     
S_(H-1)     
d_1 l_1     
...     
d_N l_N
・ 1 行目にはマップの行数を表す整数 H , マップの列数を表す整数 W , 現在の y, x 座標を表す sy sx , 移動する回数 N が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、マップの i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、マップの i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)
・ 続く N 行のうち i 行目 (1 ≦ i ≦ N) には、i 回目の移動の向き d_i と移動するマス数 l_i が与えられます。入力値最終行の末尾に改行が1つ入ります。
期待する出力
M (1 ≦ M ≦ N+1) 行の出力

・ k (1 ≦ k ≦ M) 回目の移動後の y , x 座標、y_k, x_k を出力してください。
・ M 回目で移動しきれない場合、移動できるところまで移動した後の y , x 座標、y_M, x_M を出力した後に “Stop” を出力してください。

y_1 x_1
...
y_k x_k
...
y_M x_M

または

y_1 x_1
...
y_k x_k
...
y_M x_M
Stop
条件

すべてのテストケースにおいて、以下の条件をみたします。

・ 1 ≦ H, W ≦ 20
・ 1 ≦ N ≦ 100
・ 0 ≦ sy < H, 0 ≦ sx < W
・ 1 ≦ l_i ≦ 20
・ S_i は W 文字の文字列
・ マップ上の(sy, sx)のマスは必ず '.'
・ S の各文字は '.' または '#'
・ d_i は、L, R のいずれかであり、それぞれ 左・右 を意味します。
入力例1

10 10 6 4 3
..#.....#.
..........
##.#......
#.##....#.
.##.#.....
........#.
.#......#.
.#........
...#......
#.#.......
L 2
R 1
L 4
出力例1

6 2
5 2
5 0
Stop
入力例2

15 15 6 4 7
.......#.......
....#.......#.#
.......#.....#.
.......#.#...#.
#......#.......
#.........#....
..............#
..#...#....#..#
............#..
..#...##......#
##..#..#.#.....
#..............
............#..
...#...........
.#.........#.#.
L 4
L 3
R 4
R 5
L 3
L 2
R 1
出力例2

6 0
9 0
9 0
Stop

解答と解説

少し無駄な部分があるがとりあえずできたほう

#数値として各変数に代入する
h, w, y, x, n = gets.split.map(&:to_i)

#盤面の情報をバラバラにして代入する
chart = h.times.map { gets.chomp.chars }

#移動するパターンを作成する 北 東 南 西 の順番
direction = [[-1, 0], [0, 1], [1, 0], [0, -1]]

#状態をそれぞれ定義する。最初は0
current,stop = 0,0

#コマンドがn回あるのでn回繰り返す
n.times do

 #文字列と数値をそれぞれの変数に代入
  d,move = gets.split.map.with_index { |val, i| i == 0 ? val : val.to_i }

 #Lの時−1してRのときに+1する
  d == 'L' ? current -= 1 : current += 1

 #移動する回数繰り返す
  move.times do |i|

#currentの状態によってyとxを加算する
  y += direction[current % 4][0]
  x += direction[current % 4][1]

#画面外だとアウトなのでyとxが0以上でyがh未満xがw未満であることを記述する
#移動後の座標の現在地(chart[y][x])が"."で繰り返しの最後のときに出力
  if y >= 0 && x >= 0 && y < h && x < w && chart[y][x] == "."
    puts y.to_s + " " + x.to_s if move == i + 1

#画面外や現在地が”#”のとき前の状態に戻してStopを1にして繰り返しを終わらせる
  else y -= direction[current % 4][0]
       x -= direction[current % 4][1]
       stop = 1
       break
  end
  end
# stopが1のときに現在地を出力してから改行して”Stop”を出力して繰り返しを終わらせる
  if stop == 1
     puts y.to_s + " " + x.to_s + "\n" +"Stop"
  break
end
end

PaizaAランクLvアップ問題と解説(Ruby)その5

※スキルチェック問題ではありません。
規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました

その5  座標系での向きの変わる移動 (paizaランク B 相当)

開始時点の x , y 座標、移動の回数 N が与えられます。
続くN行で移動の向き d1 … dN が与えられるので、与えられた順に移動をしたときの各移動後の x , y 座標 を答えてください。
移動者ははじめ北を向いています。

なお、マスの座標系は下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

・ 移動をするごとに向く方角が変わること
・ 移動前に向いている方角によって同じ移動の向きでも座標の変化が違うこと
の 2 点に気をつけてください。
例えば、上の図の状態から右に移動を行った場合、下の図のような状態になります。


入力される値
X Y N       
d1      
...     
dN

・ 1 行目には、開始時点の x , y 座標を表す X , Y, 移動の回数 N が与えられます。
・ 続く N 行 (1 ≦ i ≦ N) には、盤面の i 回目の移動の向きを表す文字 d_i が与えられます。入力値最終行の末尾に改行が1つ入ります。

期待する出力
N 行での出力

・ 各移動後の x , y 座標を出力してください。

x_1 y_1
...
x_N y_N
条件
すべてのテストケースにおいて、以下の条件をみたします。
・ -100 ≦ X, Y ≦ 100
・ 1 ≦ N ≦ 100
・ d は、L, R のいずれかでそれぞれ 左・右 に 1 マス進むことを表す。
入力例1

3 5 1
L
出力例1

2 5
入力例2

-18 45 6
L
L
R
R
L
R
出力例2

-19 45
-19 46
-20 46
-20 45
-21 45
-21 44

解答と解説

x, y, n = gets.split.map(&:to_i)

#初期位置が北なので以下のように配置[北, 東, 南, 西]
operate = [[-1, 0], [0, 1], [1, 0], [0, -1]]

#LとRで移動したときの状態を表す
current = 0
n.times do
  d = gets.chomp

#LかRかで状態を変える
  d == 'L' ? current -= 1 : current += 1

#状態によって移動を変更する
  y += operate[current % 4][0]
  x += operate[current % 4][1]
  puts x.to_s + ' ' + y.to_s
end

※ポイント

初期状態が北向きなのでRだったら東で[0, 1]に進み
Lだったら西なので[0, -1]に進む
-1 % 4 は 3
-3 % 4 は 1
1 % 4 は 1
3 % 4 は 3
となるので
d == ‘L’ ? now -= 1 : now += 1
でnowの値を変えるだけで移動操作が表現できる。

その6に続きます

PaizaAランクLvアップ問題と解説(Ruby)その4

その3から続いてます

※スキルチェック問題ではありません。

規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました。

3:座標系での移動・向き (paizaランク B 相当)

開始時点の y , x 座標 と向いている方角 D が与えられます。
続く 1 行で移動の向き d が与えられるので、その向きに移動した後の y , x 座標 を答えてください。
移動前に向いている方角によって同じ移動の向きでも座標の変化が違うことに気をつけてください。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。
以下の図を参考にしてみてください。




入力される値
Y X D       
d

・ 1 行目には、開始時点の y , x 座標を表す Y , X, 現在の向いている方角を表す文字 D が与えられます。
・ 2 行目には、移動の向きを表す文字 d が与えられます。

期待する出力
1 行での出力

・ 移動した後の y , x 座標を出力してください。

y x

 条件

すべてのテストケースにおいて、以下の条件をみたします。
・ -100 ≦ X, Y ≦ 100
・ D は、N, S, E, W のいずれかでそれぞれ 北・南・東・西 を意味する。
・ d は、L, R のいずれかでそれぞれ 左・右 に 1 マス進むことを表す。

入力例1

4 2 N
R

出力例1

4 3

入力例2

6 9 E
R

出力例2

7 9

解答と解説

#1行目の入力値を各変数に代入yとxは数値に変換してDは文字列として代入
y, x, D = gets.split.map.with_index { |val, i| i != 2 ? val.to_i : val }

#移動の向きを表す文字を代入
d = gets.chomp

#Rが来たときの移動パターンを設定する
operateR = { N: [0, 1], S: [0, -1], E: [1, 0], W: [-1, 0] } 
#Lが来たときの移動パターンを設定する
operateL ={ N: [0, -1], S: [0, 1], E: [-1, 0], W: [1, 0] } 
  if d == "R"
    y += operateR[D.to_sym][0] 
    x += operateR[D.to_sym][1] 
  elsif d == "L"
    y += operateL[D.to_sym][0] 
    x += operateL[D.to_sym][1] 
  end
#移動後の座標を出力する
puts y.to_s + ' ' + x.to_s

別解 3項演算子でLとRのときに値を変数に代入するだけ

y, x, now = gets.split.map.with_index { |val, i| i != 2 ? val.to_i : val }
d = gets.chomp
d == 'L' ? direction = -1 : direction = 1
if now == 'N'
  x += direction
elsif now == 'S'
  x -= direction
elsif now == 'E'
  y += direction
elsif now == 'W'
  y -= direction
end
puts y.to_s + ' ' + x.to_s

4:座標系での規則的な移動 (paizaランク B 相当)

開始時点の x , y 座標と移動の歩数 N が与えられます。
以下の図のように時計回りに渦を巻くように移動を N 歩行った後の x , y 座標 を答えてください。

なお、マスの座標系は下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

入力される値
X Y N

・ 1 行で、開始時点の x , y 座標を表す X , Y, 移動の歩数 N が与えられます。
入力値最終行の末尾に改行が1つ入ります。

期待する出力
1行での出力

・ 移動を N 歩行った後の x , y 座標を出力してください。

x y
条件
すべてのテストケースにおいて、以下の条件を満たします。
・ -100 ≦ X, Y ≦ 100
・ 0 ≦ N ≦ 100
入力例1

0 0 3
出力例1

0 1
入力例2

38 47 27
出力例2

41 47

解答

 

 

 

その5に続きます