AOC第5天 - 供应堆栈
#编程 #教程 #go #adventofcode

供应堆

Question

欢迎使用AOC的第5天,又名,令人讨厌的AF解析日。

我们是起重机操作员!
我们想要什么?!更多堆叠和板条箱!

无论如何,我们被要求使用我们的疯狂起重机技能

执行一系列精致的制造商

我们的输入给我们如下

    [D]    
[N] [C]    
[Z] [M] [P]
 1   2   3 

move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2

上半年代表堆栈和板条箱的当前状态。
第二个是我们需要执行的一系列动作。

板条箱只能一次移动!

解析

我们如何将输入解析为有意义的事物?
好吧,首先让我们使用
的两半

parts := strings.Split(raw, "\n\n")
rawStacks := parts[0]
rawMoves := parts[1]

现在让我们来解析板条箱!

我们可以使用它的一种方式就是将我们的输入刷新到一个对象中,但是在哪里有趣?!

如果我们仔细观察我们的意见,我们可能会注意到字母适合四个字符的块,例如


 0 | 1 | 2 | // positions in chunks array
xxxxxxxxxxxx
    [D]    
[N] [C]    
[Z] [M] [P]
 1   2   3 
             // row 1 col 1,2,3
chunks -> [ ["   ","[D] ","   "] ...]

武装这些信息,我们可以编写以下代码以将板条板板板板板和对正确的堆栈进行分类

func parseStacks(crates []string) [][]string {
    stacks := make([][]string, 9)
    for _, row := range crates {
        rowOfCrates := chunkBy(strings.Split(row, ""), 4)
        for crateNo, crateCandidate := range rowOfCrates {
            for _, char := range crateCandidate {
                if char >= "A" && char <= "Z" {
                    //pre-appending an element to array
                    stacks[crateNo] = append([]string{char}, stacks[crateNo]...)
                }
            }
        }
    }
    return stacks
}

让我们查看指令列表,每行代表一个命令move x from y to z,让我们创建一个结构来填充它,并将我们的列表变成这些结构的列表

type Instruction struct {
    amount int
    from   int
    to     int
}

func toInts(fromStrings []string) (result []int) {
    for _, n := range fromStrings {
        num, _ := strconv.Atoi(n)
        result = append(result, num)
    }
    return result
}

func parseInstructions(rawInstructions []string) (instructions []Instruction) {
    matcher := regexp.MustCompile(`move (\d+) from (\d+) to (\d+)`)
    for _, line := range rawInstructions {
        match := toInts(matcher.FindStringSubmatch(line)[1:])
        instructions = append(instructions, Instruction{
            amount: match[0],
            from:   match[1] - 1,
            to:     match[2] - 1,
        })
    }

    return instructions
}

组合parseInstructionsparseStacks以获取我们的parse函数

func parse(raw string) ([][]string, []Instruction) {
    chunks := strings.Split(string(raw), "\n\n")
    rawCrates := strings.Split(chunks[0], "\n")
    rawInstructions := strings.Split(chunks[1], "\n")

    return parseStacks(rawCrates), parseInstructions(rawInstructions)
}

在Go中您可以拥有more than one return value

第1部分

执行指令列表,然后构造一个从每个堆栈的顶部板条箱中构建的字符串,例如在我们当前的示例中其NDP,但是在应用了指令后,其CMZ

使用我们的解析说明和堆栈,解决方案变得很琐碎

func Part1(raw string) string {

    stacks, instructions := parse(string(raw))

    // applying instructions
    for _, instruction := range instructions {
        from := instruction.from
        to := instruction.to
        // another approach is to create a slice of size amount from `moveFrom`
        // reverse that slice and push it to `moveTo` but the approach here is much simpler to reason about
        for i := 0; i < instruction.amount; i++ {
            stacks[to] = append(stacks[to], stacks[from][len(stacks[from])-1])
            stacks[from] = stacks[from][:len(stacks[from])-1]
        }
    }

    answer := ""
    for _, stack := range stacks {
        if len(stack) > 0 {
            answer += stack[len(stack)-1]
        }
    }

    return answer

}

就是第一部分,除了解析非常简单。

第2部分

我们的堆栈有些问题,我们去确保我们的起重机是Cratemover 9000,但这是9001型号!这意味着我们可以一次移动多个板条箱。

在我们编写的代码的上下文中,这的含义是,在移动板条箱时,我们不需要保留类似堆栈的顺序,我们的源起重机的顶部要素将保持在顶部,而不是位于底部我们正在移动的板条箱

func Part2(raw string) string {
    stacks, instructions := parse(string(raw))
    // mutating the stacks
    for _, instruction := range instructions {
        from := instruction.from
        to := instruction.to
        amount := instruction.amount
        takeRange := len(stacks[from]) - amount
        // take items from `takeRange` until the end of the slice and append them to the target stack
        stacks[to] = append(stacks[to], stacks[from][takeRange:]...)
        // remove items that come after the `takeRange` from our source crate
        stacks[from] = stacks[from][:takeRange]
    }

    answer := ""
    for _, stack := range stacks {
        if len(stack) > 0 {
            answer += stack[len(stack)-1]
        }
    }

    return answer

}

就是AOC的第5天,希望您喜欢阅读它,并随时建议改进我的可怜的GO或解决方案

您可以找到完整的代码here
感谢您的阅读!