{ }codelper
列挙型switch 文P0077

カメが向いている方角

カメが一匹、(0, 0) で北を向いて立っている。
北に一歩なら y が 1 増え、東なら x が 1 増え、南なら y が 1 減り、西なら x が 1 減る。
一行に命令の文字が空白なしで与えられる。
F は向いている方へ一歩、R は右に 90 度回る、L は左に 90 度回る、B はその場で後ろを向く(180 度)。
命令をすべて終えたあと、カメの x、y と向いている方角の頭文字(N・E・S・W のどれか)を空白一つで区切って出力せよ。
方角は北・東・南・西の四つの値を時計回りの順に持つ列挙型(enum)にして解いてみよう。

入力
命令の長さは 1 以上 100,000 以下、文字は F・R・L・B のどれか
出力
一行:x y 方角
例 1 · stdin
FFRFFLFBFF
stdout
2 1 S
例 2 · stdin
LF
stdout
-1 0 W
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 6 言語の解答

この問題は列挙型で解ける Python・TypeScript・Java・C#・C・C++ だけを受け付けます。
Python は標準モジュール enum の class Dir(Enum) で作ります。
JavaScript には列挙型の構文がなく(Object.freeze({...}) でまねるだけ)外れ、Craykon にも列挙型はありません。
TypeScript の enum Dir { North, East, South, West } は値が 0〜3 の数値列挙型なので、(d + 1) % 4 のように数で計算してから as Dir で戻し、逆引き表 Dir[d] が名前 "North" をくれます。
enum は型だけの構文ではなく、実行時に使うオブジェクトを作り出します。
列挙型の中身は言語ごとに違います。
C の enum Dir { NORTH, EAST, SOUTH, WEST } は名前の付いた int 定数 0〜3 にすぎず、実行中は名前を知らないので "NESW"[d] のように名札を自分で持ちます。
C++ の enum class は Dir::North と書き、int に勝手に変わらないので static_cast<int>(d) が要ります。
Java の enum はクラスで values()、ordinal()、name() を持ち、C# は (Dir)(((int)d + 1) % 4) のように行き来して d.ToString() が名前をくれます。
Python は Dir(値)、d.value、d.name です。
落とし穴三つ:① 左回りを (d - 1) % 4 と書くと、北(0)から C・C++・Java・C#・TypeScript では -1 になります(余りは割られる数の符号に従います)。
C# は (Dir)(-1) もエラーなしで作り "-1" を出し、TypeScript も -1 as Dir を止めないので Dir[-1] が undefined になります。
(d + 3) % 4 と書きます(Python は -1 % 4 == 3 なので大丈夫)。
② C・C++(と Java の古い case: switch)で break を忘れると次の case に落ちて、一歩で何度も動きます。
C# はこれをコンパイルエラーで防ぎ、Java の矢印 switch case NORTH -> y++; は落ちません。
③ Python の Enum のメンバは数と等しくないので d == 0 はいつも False です(IntEnum なら True)。
d == Dir.NORTH か d.value で比べます。

Pythonmain.py · 23 行
from enum import Enum  class Dir(Enum):  # 시계 방향 차례    NORTH = 0    EAST = 1    SOUTH = 2    WEST = 3  STEP = {Dir.NORTH: (0, 1), Dir.EAST: (1, 0), Dir.SOUTH: (0, -1), Dir.WEST: (-1, 0)}TURN = {"R": 1, "B": 2, "L": 3}  # 왼쪽 = 오른쪽으로 세 번 x = y = 0d = Dir.NORTHfor c in input().strip():    if c == "F":        dx, dy = STEP[d]        x += dx        y += dy    else:        d = Dir((d.value + TURN[c]) % 4)  # 수로 바꿔 계산하고 다시 열거형으로print(x, y, d.name[0])
TypeScriptmain.ts · 34 行
import { readFileSync } from "node:fs"; enum Dir { // 시계 방향 차례: North = 0, East = 1, South = 2, West = 3  North,  East,  South,  West,} // 숫자 enum은 수로 계산할 수 있다. 결과를 다시 Dir로 돌려놓는다function turn(d: Dir, k: number): Dir {  return ((d + k) % 4) as Dir; // 왼쪽은 -1 대신 +3} const s = readFileSync(0, "utf8").trim();let x = 0;let y = 0;let d = Dir.North;for (const c of s) {  switch (c) {    case "R": d = turn(d, 1); break;    case "B": d = turn(d, 2); break;    case "L": d = turn(d, 3); break;    default:      switch (d) {        case Dir.North: y++; break;        case Dir.East: x++; break;        case Dir.South: y--; break;        case Dir.West: x--; break;      }  }}// 역방향 표 Dir[d]가 이름("South")을 준다console.log(`${x} ${y} ${Dir[d].charAt(0)}`);
JavaMain.java · 33 行
import java.io.BufferedReader;import java.io.InputStreamReader; public class Main {    enum Dir { NORTH, EAST, SOUTH, WEST } // 시계 방향 차례     static Dir turn(Dir d, int k) {        Dir[] all = Dir.values();        return all[(d.ordinal() + k) % 4];    }     public static void main(String[] args) throws Exception {        String s = new BufferedReader(new InputStreamReader(System.in)).readLine().trim();        int x = 0, y = 0;        Dir d = Dir.NORTH;        for (char c : s.toCharArray()) {            switch (c) {                case 'R' -> d = turn(d, 1);                case 'B' -> d = turn(d, 2);                case 'L' -> d = turn(d, 3);                default -> {                    switch (d) { // 화살표 switch는 아래로 흘러내리지 않는다                        case NORTH -> y++;                        case EAST -> x++;                        case SOUTH -> y--;                        case WEST -> x--;                    }                }            }        }        System.out.println(x + " " + y + " " + d.name().charAt(0));    }}
C#Main.cs · 34 行
using System; enum Dir { North, East, South, West } // 시계 방향 차례 class Program{    static Dir Turn(Dir d, int k) => (Dir)(((int)d + k) % 4); // 열거형 ↔ int는 명시적으로 바꾼다     static void Main()    {        string s = Console.ReadLine().Trim();        int x = 0, y = 0;        Dir d = Dir.North;        foreach (char c in s)        {            switch (c)            {                case 'R': d = Turn(d, 1); break;                case 'B': d = Turn(d, 2); break;                case 'L': d = Turn(d, 3); break;                default:                    switch (d)                    {                        case Dir.North: y++; break;                        case Dir.East: x++; break;                        case Dir.South: y--; break;                        case Dir.West: x--; break;                    }                    break;            }        }        Console.WriteLine($"{x} {y} {d.ToString()[0]}");    }}
C++main.cpp · 34 行
#include <iostream>#include <string> enum class Dir { North, East, South, West };  // 범위 있는 열거형: Dir::North로 쓰고 int로 저절로 바뀌지 않는다 Dir turn(Dir d, int k) {    return static_cast<Dir>((static_cast<int>(d) + k) % 4);} char name(Dir d) {    return "NESW"[static_cast<int>(d)];  // C++도 열거형의 이름을 실행 중에 모른다} int main() {    std::string s;    std::cin >> s;    int x = 0, y = 0;    Dir d = Dir::North;    for (char c : s) {        switch (c) {        case 'R': d = turn(d, 1); break;        case 'B': d = turn(d, 2); break;        case 'L': d = turn(d, 3); break;        default:            switch (d) {            case Dir::North: y++; break;            case Dir::East:  x++; break;            case Dir::South: y--; break;            case Dir::West:  x--; break;            }        }    }    std::cout << x << " " << y << " " << name(d) << "\n";}
Cmain.c · 32 行
#include <stdio.h> enum Dir { NORTH, EAST, SOUTH, WEST };  /* 이름 붙은 int 상수 0, 1, 2, 3 */ static const char NAME[] = "NESW";      /* C 열거형은 실행 중에 이름을 모른다: 직접 둔다 */ enum Dir turn(enum Dir d, int k) {    return (enum Dir)((d + k) % 4);} int main(void) {    static char s[100002];    scanf("%100001s", s);    int x = 0, y = 0;    enum Dir d = NORTH;    for (int i = 0; s[i]; i++) {        switch (s[i]) {        case 'R': d = turn(d, 1); break;        case 'B': d = turn(d, 2); break;        case 'L': d = turn(d, 3); break;        default:            switch (d) {            case NORTH: y++; break;  /* break가 없으면 다음 case로 흘러내린다 */            case EAST:  x++; break;            case SOUTH: y--; break;            case WEST:  x--; break;            }        }    }    printf("%d %d %c\n", x, y, NAME[d]);    return 0;}