Я вижу тут всем нравятся подобные темы в стиле kompospec.
Задача: реализовать элегентное раскрытие фигурных скобок как в баше:
Условия:
- есть некая функция
expand
, приниающая строку, раскрывающая фигурные скобки с вариантами, перечисленными через запятую, и возвращающая массив с результатами. Пример реализации:
def combine(a: list, b: list) -> list:
rv = []
for x in a:
for y in b:
rv.append(x + y)
return rv
def append(lst: list[str], add: str) -> list[str]:
return [x + add for x in lst]
def expand(s: str) -> list[str]:
head, *parts = s.split("{")
rv = [head]
for part in parts:
options, rest = part.split("}", 1)
rv = append(combine(rv, options.split(",")), rest)
return rv
assert expand("~/{.local/share,.config,.cache}/{foo,bar}-package") == ['~/.local/share/foo-package', '~/.local/share/bar-package', '~/.config/foo-package', '~/.config/bar-package', '~/.cache/foo-package', '~/.cache/bar-package']
- Язык реализации может быть любой.
- Желательно чтобы функция могла обрабатывать вложенные скобки. Можно так же добавить поддержку срезов типа
{a..z}
и предусмотреть экранирование символово{},.
- Предпочтительнее декларативный стиль.
Дополнения:
Что за вложенные скобки? - А вот они:
~
❯ echo /path/to/{foo,ba{r,z}}.txt
/path/to/foo.txt /path/to/bar.txt /path/to/baz.txt
Примеры решений
Рекурсивный вызов:
# Модифицированная версия отсюда https://algo.monster/liteproblems/1096
def parse_range(s: str) -> list[str | int]:
range_parts = s.split("..", 1)
is_num_range = all(x.isdigit() for x in range_parts)
first, last = map((ord, int)[is_num_range], range_parts)
# first, last = min(first, last), max(first, last)
res = range(first, last + 1)
return list(res if is_num_range else map(chr, res))
def expand(s: str) -> set[str]:
rv = set()
def dfs(exp: str) -> None:
try:
inner, after = exp.split("}", 1)
except ValueError:
rv.add(exp)
return
before, inner = inner.rsplit("{", 1)
for item in parse_range(inner) if ".." in inner else inner.split(","):
dfs(f"{before}{item}{after}")
dfs(s)
return rv
Недостаток: без set
будут дублироваться элементы.
Функциональный стиль:
def expand str
_, res, _ = str.each_char.reduce([[''], [''], []]) do |(pre, cur, acc), c|
case c
when '}'; [acc + cur, acc + cur, []]
when '{'; [cur, cur, acc]
when ','; [pre, pre, acc + cur]
else ; [pre, cur.map { |x| x + c }, acc]
end
end
res
end
Недостаток: неправильно обрабатывает запятую на первом уровне вложенности.