Подтвердить что ты не робот

Распаковывать ломтики по заданию?

Есть ли в Go элегантный способ выполнять несколько назначений из массивов, как в Python? Вот пример того, что я пытаюсь сделать на Python (разбить строку, а затем присвоить полученный массив двум переменным).

python:
>>> a, b = "foo;bar".split(";")

Мое текущее решение:

x := strings.Split("foo;bar", ";")
a, b := x[0], x[1]

Я вижу, что это становится грязным в некоторых конструкциях. Практический пример, с которым я сейчас сталкиваюсь, - это разбор файла закладки и назначение его карте:

bookmark := make(map[string]string)
x := strings.Split("foo\thttps://bar", "\t")
name, link := x[0], x[1]
bookmark[name] = link

Теперь у меня есть бесполезная переменная x. Я хотел бы сделать что-то вроде:

bookmark := make(map[string]string)
name, line := strings.Split("foo\thttps://bar", "\t")
bookmark[name] = link

но это недействительно.

4b9b3361

Ответ 1

Как отметил Серджио Туленцев, общая упаковка/распаковка, как это делается на Python, не поддерживается. Я думаю, что путь туда - это определить свою собственную небольшую ad-hoc-функцию, используя несколько возвращаемых значений:

func splitLink(s, sep string) (string, string) {
    x := strings.Split(s, sep)
    return x[0], x[1]
}

И вы можете написать:

name, link := splitLink("foo\thttps://bar", "\t")

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

- EDIT -

Другой способ распаковки массива - переменные аргументы указателя:

func unpack(s []string, vars... *string) {
    for i, str := range s {
        *vars[i] = str
    }
}

Что вы можете написать:

var name, link string
unpack(strings.Split("foo\thttps://bar", "\t"), &name, &link)
bookmarks[name] = link

Это будет работать для любого размера массива, но это, возможно, менее читаемо, и вы должны явно объявлять свои переменные.

Ответ 2

Если ваша функция предназначена для разделения строки только по первому появлению разделителя, вы всегда можете создать свою собственную функцию:

package main

import (
    "fmt"
    "strings"
)

func Split(s, sep string) (string, string) {
    // Empty string should just return empty
    if len(s) == 0 {
        return s, s
    }   

    slice := strings.SplitN(s, sep, 2)

    // Incase no separator was present
    if len(slice) == 1 {
        return slice[0], ""
    }

    return slice[0], slice[1]
}

func main() {
    a, b := Split("foo;bar;foo", ";")
    fmt.Println(a, b)
}

Выход:

foo bar;foo

Игровая площадка

Ответ 3

Вы также можете использовать анонимные функции:

a, b := func() (string, string) {
    x := strings.Split("foo;bar", ";")
    return x[0], x[1]
}()

Примечание: не забывайте () на конце закрывающей скобки }, иначе вы получите ошибку:

assignment mismatch: 2 variable but 1 values

Это связано с тем, что без () возвращается функция (1 значение), а не ожидаемые строки (2 значения).