{ }codelper
declaring variablesscopeconstantsP0087

Blocks and shadowed variables

Run an n-line program written in a tiny language, line by line.
At the start only the outermost block is open.
Each line is one of: - {: open a new block (inside the current one). - }: close the innermost block.
Every name declared in it disappears, and any outer name it was hiding becomes visible again. - let x v: declare variable x with value v in the current block.
If an outer block has the same name, this one hides it until this block closes. - const x v: like let, but x is a constant. - set x v: store v in the x that is visible now (the x in the innermost block that has one).
If that x is a constant, nothing changes.
It never creates a new name. - print x: print the value of the visible x on its own line. Nothing except print produces output (with no print at all, the output is empty).

Input
First line n (1 ≤ n ≤ 1000), then n command lines.
Names are 1 to 10 lowercase letters, -10^9 ≤ v ≤ 10^9.
The outermost block is never closed, and at most 100 blocks are open at once.
A name is never declared twice in the same block, and the names in set and print are always visible.
Output
One line per print
Example 1 · stdin
11
let x 1
const y 2
{
let x 10
set y 5
set x 11
print x
print y
}
print x
set x -3
stdout
11
2
1
main.py
Input (stdin)

When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).

$ Ready

Open this on a computer to write and run code. On mobile you can read the problem and the solutions.

In other languagesShow solutions by language · 8 languages

Block scope differs between languages.
JS/TS let/const and local variables in C/C++ make a new name per block as in this problem, and an inner block may hide an outer name (old JS var ignores blocks and is one per function).
Java and C# reject redeclaring, in an inner block, a local that is still alive (Java "already defined", C# CS0136).
In Python if and for do not create a scope; only functions do.
So handling everything with one dictionary, ignoring blocks, lets the inner let x 10 overwrite the outer x.
In Craykon a variable first made inside a block is not visible outside it, but assigning to a name that already exists outside changes the outer variable (there is no separate shadowing declaration).
Constants differ too: real languages refuse a second assignment (JS TypeError at run time, TS TS2588, compile errors for Java final, C# const and C/C++ const; Python does not stop it, and Craykon has no constants).
In this problem it is silently ignored.
The solution is a stack with one dictionary per block (Python dict, JS/TS Map, Java HashMap, C# Dictionary, C++ unordered_map, Craykon Dict()).
C has no map, so push declarations onto an array with their block depth and drop them at }.
Trap: search from the innermost block (the end of the stack).
Searching from the outside, not dropping a block's names at }, or treating set as 'add to the current block' (which is what assignment inside a Python function does) gives wrong values after a block closes.

Pythonmain.py · 25 lines
# 블록마다 사전 하나: 이름 → [값, 상수인가]. 맨 끝이 지금 블록scopes = [{}]  def find(name):    for scope in reversed(scopes):  # 안쪽 블록부터        if name in scope:            return scope[name]  n = int(input())for _ in range(n):    cmd, *rest = input().split()    if cmd == "{":        scopes.append({})    elif cmd == "}":        scopes.pop()    elif cmd in ("let", "const"):        scopes[-1][rest[0]] = [int(rest[1]), cmd == "const"]    elif cmd == "set":        var = find(rest[0])        if not var[1]:            var[0] = int(rest[1])    else:        print(find(rest[0])[0])
JavaScriptmain.js · 24 lines
const lines = require("fs").readFileSync(0, "utf8").split("\n"); // 블록마다 Map 하나: 이름 → { value, isConst }. 맨 끝이 지금 블록const scopes = [new Map()]; function find(name) {  for (let i = scopes.length - 1; i >= 0; i--) {    if (scopes[i].has(name)) return scopes[i].get(name);  }} const n = Number(lines[0]);const out = [];for (let i = 1; i <= n; i++) {  const [cmd, name, v] = lines[i].trim().split(" ");  if (cmd === "{") scopes.push(new Map());  else if (cmd === "}") scopes.pop();  else if (cmd === "let" || cmd === "const") scopes.at(-1).set(name, { value: Number(v), isConst: cmd === "const" });  else if (cmd === "set") {    const entry = find(name);    if (!entry.isConst) entry.value = Number(v);  } else out.push(find(name).value);}console.log(out.join("\n"));
TypeScriptmain.ts · 32 lines
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n"); interface Entry {  value: number;  readonly isConst: boolean;} // 블록마다 Map 하나. 맨 끝이 지금 블록const scopes: Map<string, Entry>[] = [new Map()]; function find(name: string): Entry {  for (let i = scopes.length - 1; i >= 0; i--) {    const entry = scopes[i].get(name);    if (entry !== undefined) return entry;  }  throw new Error(`${name} is not declared`); // 문제에서 일어나지 않는다} const n: number = Number(lines[0]);const out: number[] = [];for (let i = 1; i <= n; i++) {  const [cmd, name, v] = lines[i].trim().split(" ");  if (cmd === "{") scopes.push(new Map());  else if (cmd === "}") scopes.pop();  else if (cmd === "let" || cmd === "const") scopes[scopes.length - 1].set(name, { value: Number(v), isConst: cmd === "const" });  else if (cmd === "set") {    const entry = find(name);    if (!entry.isConst) entry.value = Number(v);  } else out.push(find(name).value);}console.log(out.join("\n"));
JavaMain.java · 51 lines
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map; public class Main {    static class Entry {        int value;        final boolean isConst;         Entry(int value, boolean isConst) {            this.value = value;            this.isConst = isConst;        }    }     // 블록마다 HashMap 하나. 맨 끝이 지금 블록    static final List<Map<String, Entry>> scopes = new ArrayList<>();     static Entry find(String name) {        for (int i = scopes.size() - 1; i >= 0; i--) {            Entry e = scopes.get(i).get(name);            if (e != null) return e;        }        return null; // 문제에서 일어나지 않는다    }     public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(br.readLine().trim());        scopes.add(new HashMap<>());        StringBuilder out = new StringBuilder();        for (int i = 0; i < n; i++) {            String[] p = br.readLine().trim().split(" ");            switch (p[0]) {                case "{" -> scopes.add(new HashMap<>());                case "}" -> scopes.remove(scopes.size() - 1);                case "let", "const" -> scopes.get(scopes.size() - 1).put(p[1], new Entry(Integer.parseInt(p[2]), p[0].equals("const")));                case "set" -> {                    Entry e = find(p[1]);                    if (!e.isConst) e.value = Integer.parseInt(p[2]);                }                default -> out.append(find(p[1]).value).append('\n');            }        }        System.out.print(out);    }}
C#Main.cs · 46 lines
using System;using System.Collections.Generic;using System.Text; class Entry{    public int Value;    public readonly bool IsConst;    public Entry(int value, bool isConst) { Value = value; IsConst = isConst; }} class Program{    // 블록마다 Dictionary 하나. 맨 끝이 지금 블록    static readonly List<Dictionary<string, Entry>> scopes = new() { new() };     static Entry Find(string name)    {        for (int i = scopes.Count - 1; i >= 0; i--)            if (scopes[i].TryGetValue(name, out Entry e)) return e;        return null; // 문제에서 일어나지 않는다    }     static void Main()    {        int n = int.Parse(Console.ReadLine());        var sb = new StringBuilder();        for (int i = 0; i < n; i++)        {            string[] p = Console.ReadLine().Trim().Split(' ');            switch (p[0])            {                case "{": scopes.Add(new()); break;                case "}": scopes.RemoveAt(scopes.Count - 1); break;                case "let":                case "const": scopes[^1][p[1]] = new Entry(int.Parse(p[2]), p[0] == "const"); break;                case "set":                    Entry e = Find(p[1]);                    if (!e.IsConst) e.Value = int.Parse(p[2]);                    break;                default: sb.Append(Find(p[1]).Value).Append('\n'); break;            }        }        Console.Write(sb);    }}
C++main.cpp · 45 lines
#include <iostream>#include <string>#include <unordered_map>#include <vector> struct Var {    int value;    bool isConst;}; // 블록마다 사전 하나. back()이 지금 블록std::vector<std::unordered_map<std::string, Var>> scopes(1); Var* find(const std::string& name) {    for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) {        auto found = it->find(name);        if (found != it->end()) return &found->second;    }    return nullptr;  // 문제에서 일어나지 않는다} int main() {    int n;    std::cin >> n;    for (int i = 0; i < n; i++) {        std::string cmd, name;        int v;        std::cin >> cmd;        if (cmd == "{") {            scopes.emplace_back();        } else if (cmd == "}") {            scopes.pop_back();        } else if (cmd == "let" || cmd == "const") {            std::cin >> name >> v;            scopes.back()[name] = Var{v, cmd == "const"};        } else if (cmd == "set") {            std::cin >> name >> v;            Var* p = find(name);            if (!p->isConst) p->value = v;        } else {            std::cin >> name;            std::cout << find(name)->value << '\n';        }    }}
Cmain.c · 51 lines
#include <stdio.h>#include <string.h> /* C에는 사전이 없다: 선언을 차례로 쌓고, 블록이 닫히면 그 블록 것만 걷어 낸다 */struct Var {    char name[11];    int value;    int is_const;    int depth;}; static struct Var vars[1000];static int count = 0; /* 맨 뒤(가장 안쪽)부터 찾는다 */static struct Var *find(const char *name) {    for (int i = count - 1; i >= 0; i--)        if (strcmp(vars[i].name, name) == 0) return &vars[i];    return NULL; /* 문제에서 일어나지 않는다 */} int main(void) {    int n, depth = 0;    char cmd[8], name[11];    int v;    scanf("%d", &n);    for (int i = 0; i < n; i++) {        scanf("%7s", cmd);        if (strcmp(cmd, "{") == 0) {            depth++;        } else if (strcmp(cmd, "}") == 0) {            while (count > 0 && vars[count - 1].depth == depth) count--;            depth--;        } else if (strcmp(cmd, "let") == 0 || strcmp(cmd, "const") == 0) {            scanf("%10s %d", name, &v);            struct Var *p = &vars[count++];            strcpy(p->name, name);            p->value = v;            p->is_const = cmd[0] == 'c';            p->depth = depth;        } else if (strcmp(cmd, "set") == 0) {            scanf("%10s %d", name, &v);            struct Var *p = find(name);            if (!p->is_const) p->value = v;        } else {            scanf("%10s", name);            printf("%d\n", find(name)->value);        }    }    return 0;}
Craykonmain.cray · 49 lines
// 블록마다 사전 두 개: 값과 '상수인가'. 맨 끝이 지금 블록이다값들 = [Dict()];상수들 = [Dict()]; // 이름을 가진 가장 안쪽 블록의 번호(없으면 -1)찾기(이름){    for (j = 값들.Count - 1; j >= 0; j--)    {        if (값들[j].Contains(이름))        {            return j;        }    }    return -1;} n = Console.ReadLine().ToNumber();for (i = 0; i < n; i++){    parts = Console.ReadLine().Split();    cmd = parts[0];    switch (cmd)    {        case "{":            값들.Add(Dict());            상수들.Add(Dict());            break;        case "}":            값들.RemoveAt(값들.Count - 1);            상수들.RemoveAt(상수들.Count - 1);            break;        case "let":        case "const":            값들[값들.Count - 1][parts[1]] = parts[2].ToNumber();            상수들[상수들.Count - 1][parts[1]] = cmd == "const";            break;        case "set":            b = 찾기(parts[1]);            if (!상수들[b][parts[1]])            {                값들[b][parts[1]] = parts[2].ToNumber();            }            break;        default:            Console.WriteLine(값들[찾기(parts[1])][parts[1]]);            break;    }}