Альтернатива хешам для обработки смешанных типов в Go - PullRequest
1 голос
/ 03 мая 2019

Я делаю упражнение по программированию, чтобы познакомиться с Go.В настоящее время я пишу синтаксический анализатор, который анализирует строку в команде с аргументами, например:

C w h           Should create a new canvas of width w and height h.
B x y c         Should fill the entire area connected to (x,y) with "colour" c.
Q               Should quit the program.

Сначала я начал использовать хэши для хранения аргументов, например, w h.Но это негибко, и, как вы видите, c - это цвет, который будет строкой, в то время как другие аргументы являются целыми числами.

Я начал так:

package main

import (
    "errors"
    "strconv"
    "strings"
)

type command struct {
    id   string
    args map[string]int // Won't work because args can be of mixed types
}

func parseCommand(input string) (command, error) {
    if input == "" {
        return command{}, errors.New("No input")
    }

    commandParts := strings.Split(input, " ")

    switch commandParts[0] {
    case "C":
        if (len(commandParts)) != 3 {
            return command{}, errors.New("C (create) requires 2 arguments")
        }

        w, err := strconv.Atoi(commandParts[1])

        if err != nil {
            return command{}, errors.New("width must be an integer")
        }

        h, err := strconv.Atoi(commandParts[2])

        if err != nil {
            return command{}, errors.New("height must be an integer")
        }

        return command{
            id: "create",
            args: map[string]int{
                "w": w,
                "h": h,
            },
        }, nil
    case "B":
        if (len(commandParts)) != 4 {
            return command{}, errors.New("B (Bucket Fill) requires 3 arguments")
        }

        x, err := strconv.Atoi(commandParts[1])

        if err != nil {
            return command{}, errors.New("x must be an integer")
        }

        y, err := strconv.Atoi(commandParts[2])

        if err != nil {
            return command{}, errors.New("y must be an integer")
        }

        return command{
            id: "bucketFill",
            args: map[string]int{
                "x": x,
                "y": y,
                "c": commandParts[3], // This should be a string!
            },
        }, nil
    case "Q":
        return command{
            id: "quit",
        }, nil
    default:
        return command{}, errors.New("Command not supported")
    }
}

Мой вопрос: как мне разобрать входную строку в команде, если аргументы, которые я хочу вернуть, являются переменными и имеют смешанные типы?Спасибо.

PS Команды могут свободно вводиться и изменять поддельный холст в терминале, например:

enter command: C 20 4
----------------------
|                    |
|                    |
|                    |
|                    |
----------------------

// Didn't mention this one but it's a Line if you didn't guess
enter command: L 1 2 6 2
----------------------
|                    |
|xxxxxx              |
|                    |
|                    |
----------------------

1 Ответ

4 голосов
/ 03 мая 2019

Ваш подход к command не верен. command - это то, что вы можете применить к холсту. Итак, мы говорим так:

type canvas struct{ ... }

type command interface {
    apply(canvas *canvas)
}

Теперь есть несколько видов команд, каждая со своими аргументами. Однако при использовании в качестве команды вызывающей стороне не нужно заботиться о том, что это за аргументы.

type createCommand struct {
    width  int
    height int
}

func (c createCommand) apply(canvas *canvas) { ... }

type bucketFillCommand struct {
    x     int
    y     int
    color string
}

func (c bucketFillCommand) apply(canvas *canvas) { ... }

type quitCommand struct{}

func (c quitCommand) apply(canvas *canvas) { ... }

И тогда вы можете их проанализировать (я бы, вероятно, включил весь анализ в функции, но это нормально).

func parseCommand(input string) (command, error) {
    if input == "" {
        return nil, errors.New("No input")
    }

    commandParts := strings.Split(input, " ")

    switch commandParts[0] {
    case "C":
        if (len(commandParts)) != 3 {
            return nil, errors.New("C (create) requires 2 arguments")
        }

        w, err := strconv.Atoi(commandParts[1])

        if err != nil {
            return nil, errors.New("width must be an integer")
        }

        h, err := strconv.Atoi(commandParts[2])

        if err != nil {
            return nil, errors.New("height must be an integer")
        }

        return createCommand{width: w, height: h}, nil
    case "B":
        if (len(commandParts)) != 4 {
            return nil, errors.New("B (Bucket Fill) requires 3 arguments")
        }

        x, err := strconv.Atoi(commandParts[1])

        if err != nil {
            return nil, errors.New("x must be an integer")
        }

        y, err := strconv.Atoi(commandParts[2])

        if err != nil {
            return nil, errors.New("y must be an integer")
        }

        return bucketFillCommand{x: x, y: y, color: commandParts[3]}, nil
    case "Q":
        return quitCommand{}, nil
    default:
        return nil, errors.New("Command not supported")
    }
}

Обратите внимание, что это возвращает nil в качестве команды, когда что-то выходит из строя, а не command{}.

Детская площадка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...