Uma matriz de desafios nº 2: separar uma matriz aninhada

36

Nota: Este é o número 2 em uma série de desafios de de . Para o desafio anterior, clique aqui .

Separando listas aninhadas

Para separar valores em uma lista aninhada, alise-o e, em seguida, agrupe cada valor para que ele fique na mesma profundidade aninhada de antes.

Ou seja, esta lista:

[1, [2, 3], [4, 4, [5, 2], 1]]

Se tornaria:

[1, [2], [3], [4], [4], [[5]], [[2]], [1]]

O desafio

Sua tarefa é escrever um programa que utilize qualquer lista aninhada de números inteiros positivos (dentro dos limites do seu idioma) e execute essa operação de separação.

Você pode enviar uma função que considere a lista como argumento ou um programa completo que execute E / S.

Como se trata de , o envio mais curto (em bytes) vence! *

* As brechas de golfe padrão são proibidas. Você sabe o que fazer.


Casos de teste

As listas de entrada sempre conterão números inteiros no tamanho inteiro padrão do seu idioma. Para evitar restrições de idiomas que os impedem de competir, os valores não serão aninhados em profundidades superiores a 10.

Você pode assumir que a entrada não terá sub-listas vazias: por exemplo - [[5, []]]não será fornecida. No entanto, a lista principal pode estar vazia.

[]            ->  []

[[1, 2]]      ->  [[1], [2]]
[3, [4, 5]]   ->  [3, [4], [5]]
[3, [3, [3]]] ->  [3, [3], [[3]]]
[[6, [[7]]]]  ->  [[6], [[[7]]]]
[[5, 10], 11] ->  [[5], [10], 11]

Não hesite em deixar um comentário se eu perdi uma caixa de esquina.

Exemplo

Eu joguei junto um (ungolfed) Python solução rápida 3 como um exemplo - você pode testá-lo em repl.it .

FlipTack
fonte
Adicione uma caixa de teste com números maiores que um dígito para respostas baseadas em string.
orlp
@orlp boa ideia.
FlipTack
2
Podemos assumir uma certa profundidade máxima? Digamos, 16?
orlp
@orlp Vou dizer que sim, a profundidade máxima aninhada será 10, pois estou mais interessado no seu algoritmo e execução do método do que nas restrições da sua linguagem. Atualizará o tópico agora.
FlipTack
Posso imprimir como uma string?
Rohan Jhunjhunwala

Respostas:

4

Braquilog , 16 bytes

:{##:0&:ga|g}ac|

Experimente online!

Explicação

Example input: [1:[2:3]]

:{          }a     Apply the predicate below to each element of the list: [[1]:[[2]:[3]]]
              c    Concatenate: Output = [1:[2]:[3]]
               |   Or: Input = Output = []

  ##                 Input is a list: e.g. Input = [2:3]
    :0&              Call recursively the main predicate with this input: [2:3]
       :ga           Group each element in a list: Output = [[2]:[3]]
          |          Or (not a list): e.g. Input = 1
           g         Group into a list: Output = [1]
Fatalizar
fonte
O que o Zargumento faz no TIO? Sem ele, isso parece gerar true / false, o que faz parecer Znecessário na contagem de bytes.
FlipTack
O @FlipTack Zdiz ao Brachylog que o argumento de saída é uma variável. Essa é a variável que é unificada com a saída resultante. Quando você o remove, ele informa ao Brachylog que a saída é uma variável anônima e, em vez disso, imprimirá se o predicado principal é bem-sucedido ou falha. É o mesmo que no Prolog, onde o resultado é "colocado" em uma variável.
Fatalize
Ok :) boa resposta!
FlipTack
19

Mathematica, 24 21 bytes

##&@@List/@#0/@#&/@#&

ou um destes:

##&@@List/@#&/@#0/@#&
##&@@List@*#0/@#&/@#&
##&@@List/@#&@*#0/@#&

Explicação

A razão disso é tão curto é que é basicamente uma recursão que não requer um caso base explícito.

Há muito açúcar sintático aqui, então vamos começar por isso. &denota uma função sem nome restante, cujo argumento é escrito como #. Dentro desta função #0refere-se à própria função, que permite escrever funções recursivas sem nome. Mas vamos começar dando um nome à função interna e retirando-a:

f[x_] := ##& @@ List /@ f /@ x
f /@ # &

O outro açúcar sintático importante é o f/@xque é curto, Map[f, x]ou seja, ele invoca ftodos os elementos de x. O motivo f[x_] := ... f /@ xnão leva à recursão infinita é que o mapeamento de algo sobre um átomo deixa o átomo inalterado sem realmente chamar a função. Portanto, não precisamos verificar o caso base (o elemento atual é um número inteiro) explicitamente.

Portanto, a função fprimeiro recursa para a lista mais profunda x, e nesse ponto f/@torna-se um não operacional. Então chamamos de uso ##& @@ List /@nisso. O mapeamento Listsobre a lista simplesmente agrupa cada elemento em uma lista separada, {1, 2, 3}tornando - se assim {{1}, {2}, {3}}. Em seguida, aplicamos ##& a ele, o que significa que a cabeça (ou seja, a lista externa) é substituída por ##&, então isso se transforma ##&[{1}, {2}, {3}]. Mas ##&simplesmente retorna seus argumentos como um Sequence(que você pode considerar uma lista desembrulhada ou uma espécie de operador "splat" em outros idiomas).

Então, ##& @@ List /@transforma uma lista {1, 2, 3}em {1}, {2}, {3}(mais ou menos, essa última coisa é realmente envolvida na cabeça Sequence, mas isso desaparece assim que usamos o valor em qualquer lugar).

Isso deixa a questão de por que fjá não é a solução para o desafio. O problema é que a lista mais externa deve ser tratada de maneira diferente. Se tivermos entrada {{1, 2}, {3, 4}}, queremos {{1}, {2}, {3}, {4}}e não {{1}}, {{2}}, {{3}}, {{4}} . Minha solução original corrigiu isso passando o resultado final como uma lista de argumentos para a Joinqual restauraria o nível externo de listas, mas esse apenas ignora o nível externo usando- f se em um mapa na saída. Portanto, fé aplicado apenas aos elementos individuais da lista mais externa e nunca chega a tocar nessa lista.

Quanto às outras três soluções, a primeira simplesmente aplica a recursão fora da fqual funciona da mesma forma. As outras duas soluções evitam uma Mapoperação repetida , compondo primeiro duas funções e depois mapeando o resultado apenas uma vez.

Martin Ender
fonte
8

J , 19 18 bytes

(<@]/@,~>)S:0 1{::

Este é um verbo anônimo que recebe e retorna matrizes em caixa, que são a versão de J (bastante complicada) de matrizes aninhadas. Veja passar em todos os casos de teste.

Explicação

Isso usa as operações um tanto exóticas {::( mapa ) e S:( spread ), que operam em matrizes in a box. {::substitui cada folha pelo caminho em caixa para essa folha. S:aplica um determinado verbo a uma profundidade de aninhamento e divide os resultados em uma matriz.

(<@]/@,~>)S:0 1{::  Input is y.
(        )          Let's look at this verb first.
        >           Open the right argument,
      ,~            append the left argument to it,
    /               then reduce by
 <@]                boxing. This puts the left argument into as many nested boxes
                    as the right argument is long.
                    This verb is applied to y
               {::  and its map
            0 1     at levels 0 and 1.
                    This means that each leaf of y is paired with its path,
                    whose length happens to be the nesting depth of y,
                    and the auxiliary verb is applied to them.
          S:        The results are spread into an array.
Zgarb
fonte
3

R, 199 bytes

function(l){y=unlist(l);f=function(x,d=0){lapply(x,function(y){if(class(y)=='list'){f(y,d=d+1)}else{d}})};d=unlist(f(l));lapply(1:length(d),function(w){q=y[w];if(d[w]){for(i in 1:d[w])q=list(q)};q})}

Esta pergunta foi difícil. As listas de R são um pouco estranhas e não é absolutamente fácil repetir todos os elementos das sublistas. Também não é fácil determinar a profundidade dessa lista. Em seguida, o desafio passa a ser recriar a lista com todos os elementos separados; portanto, também precisamos de uma maneira adaptativa de criar uma lista com uma certa profundidade.

A solução consiste em duas grandes partes. Uma função recursiva que percorre todas as listas e registra a profundidade:

  f=function(x,d=0){
    lapply(x,function(y){
      if(class(y)=='list'){
        f(y,d=d+1)
      } else {
        d
      }})
  }

Quando temos as profundezas de todas as entradas do vetor unlist(l)armazenadas d, criamos implicitamente uma lista lapplye a preenchemos com a seguinte função:

  lapply(1:length(d),function(w){
    q=y[w]
    if(d[w]){
      for(i in 1:d[w]){
        q=list(q)
      }
    }
    q
  })

Nesta chamada de aplicação, criamos um objeto qcom o valor da entrada na lista, verificamos sua profundidade e verificamos se é diferente de zero. Se for zero, podemos apenas deixá-lo como um valor numérico. Se for diferente de zero, precisamos aninhar essa quantidade de listas. Então, chamamos dtempos de loop for e repetidamente q=list(q).

lapplydepois coloca todos esses valores qem uma lista, criando a saída desejada.

Programa completo com espaçamento adequado e tais:

function(our.list){
  values <- unlist(our.list)
  f <- function(part.list, depth = 0){
    lapply(part.list, function(y){
      if(class(y)=='list'){
        f(y, depth <- depth + 1)
      } else {
        return(depth)
      }})
  }
  depths <- unlist(f(our.list))
  new.list <- lapply(1:length(depths), function(w){
    q <- values[w]
    if(depths[w] != 0){
      for(i in 1:depths[w]){
        q <- list(q)
      }
    }
    return(q)
  })
  return(new.list)
}
JAD
fonte
Nice one, este é o método que eu usei com a minha solução inicial Python para os casos de teste :)
FlipTack
is.list(y)em vez de class(y)=='list'? Não é possível verificar se isso realmente funcionará.
Giuseppe
180 bytes
Giuseppe
3

Retina , 34 bytes

+`(.(?>()\[|(?<-2>)]|.)+)\2,
$1],[

Experimente online!

Martin Ender
fonte
Como funciona o (?<-2>)trabalho?
Kritixi Lithos
@KritixiLithos É um grupo de equilíbrio . Ele aparece na pilha de captura 2, que permite acompanhar a profundidade atual do aninhamento.
Martin Ender
2

C (gcc), 147 bytes

d=0,l,i;
P(n,c){for(;n--;)putchar(c);}
main(c){for(;~(c=getchar());l=i)i=isdigit(c),P((l<i)*d,91),P(i,c),P((l>i)*d,93),P(l>i,32),d+=(92-c)*(c>90);}

Exemplo de entrada:

1 [23 3] [40 4 [5 2] 1]

Exemplo de saída:

1 [23] [3] [40] [4] [[5]] [[2]] [1]
orlp
fonte
2

empilhados , não-concorrentes, 25 bytes

{e d:e$wrap d 1-*}cellmap

Essa é uma função, pois modifica o membro superior da pilha. Se você deseja uma função genuína, basta adicionar [e ]ao começo e ao fim. Experimente aqui!

Aqui está uma versão legível:

{ arr :
  arr { ele depth :
    ele   $wrap depth 1- * (* execute wrap n times, according to the depth *)
  } cellmap (* apply to each cell, then collect the results in an array *)
} @:a2
(1 (2 3) (4 4 (5 2) 1)) a2 out

Caso de teste:

(1 (2 3) (4 4 (5 2) 1))    (* arg on TOS *)
{e d:e$wrap d 1-*}cellmap
out                        (* display TOS *)

Saída sem novas linhas:

(1 (2) (3) (4) (4) ((5)) ((2)) (1))
Conor O'Brien
fonte
É *como argumento para o bloco de código?
Downgoat
@Downgoat, neste caso, envolve os d-1tempos dos argumentos . $funcé uma função que pode ser manipulada.
Conor O'Brien
2

PHP, 101 94 bytes

salvou 1 byte graças a @Christoph, salvou outros 6 inspirados por isso.

function s($a){foreach($a as$b)if($b[0])foreach(s($b)as$c)$r[]=[$c];else$r[]=$b;return$r?:[];}

função recursiva, bastante direta

demolir

function s($a)
{
    foreach($a as$b)                // loop through array
        if($b[0])                       // if element is array
            foreach(s($b)as$c)$r[]=[$c];    // append separated elements to result
        else$r[]=$b;                    // else append element to result
    return$r?:[];                   // return result, empty array for empty input
}
Titus
fonte
Onde o resultado é inicializado?
Neil
@ Neil: PHP não requer inicialização explícita. De qualquer $rrecebe elementos no circuito ou a função devolve uma matriz vazia. Pode gerar avisos, mas eles não são impressos com a configuração padrão.
Titus
Isso não significa que você seria capaz de chamá-lo apenas uma vez?
Neil
1
Assim como você pode ficar louco: !cos(). cos()retorna nullpara todo array e float! = 0 para todo inteiro positivo. Quero dizer ... quem se importa com avisos?
Christoph
1
@Christoph: avisos são impressos, avisos não (na configuração padrão). Mas é uma ótima ideia! On is_int: Inverter a condição não salva nada; Eu preciso de um espaço entre elsee foreach. MAS: $b[0]para um número inteiro é NULL.
Titus
2

Python 2, 122 106 bytes

Pontuação bastante terrível, apenas uma implementação direta.

Obrigado a @Zachary T por ajudar a economizar 16 bytes!

def x(l,a=[],d=0):
 n=lambda b:b and[n(b-1)]or l
 if'['in`l`:[x(e,a,d+1)for e in l];return a
 else:a+=n(d)

Ligue xcom um argumento para executar. Por algum motivo, ele pode ser executado apenas uma vez.

Azul
fonte
Você pode mudar a+=[n(l,d)]para a+=n(l,d),(observe a vírgula à direita)
FlipTack
Você precisa atribuir a t?
Zacharý 23/12/16
Isso funciona quando você chama mais de uma vez?
Zacharý 23/12/16
Você pode passar npara a função e remover o primeiro argumento, pois sempre será l.
Zacharý
E-mail
zacharý23 /
2

JavaScript (Firefox 30-57), 53 bytes

f=a=>[for(e of a)for(d of e.map?f(e):[e])e.map?[d]:d]

A melhor resposta ES6 que tenho até agora é 76 bytes:

f=(a,r=[],d=0)=>a.map(e=>e.map?f(e,r,d+1):r.push((n=d=>d?[n(d-1)]:e)(d)))&&r
Neil
fonte
2
Nos dois blocos de código, acho que você omitiu a liderança f=.
Conor O'Brien
@ ConorO'Brien Mais uma vez ...
Neil
1

Perl 6 , 60 47 bytes

sub f{[$_~~List??|([$_] for .&f)!!$_ for |$^a]}

( Experimente online. )

Explicação:

  1. [... for |$^a]: Itere sobre a matriz de entrada e construa uma nova matriz a partir dela.
  2. $_ ~~ List ?? ... !! ...: Para cada elemento, verifique se ele próprio é uma matriz.
  3. |([$_] for .&f): Se o elemento for uma matriz, aplique recursivamente a função, itere sobre os elementos da nova matriz retornada pela chamada recursiva, envolva cada elemento em uma matriz própria e insira-os na lista externa.
  4. $_: Se o elemento não for uma matriz, passe-o como está.
smls
fonte
1

Haskell, 71 bytes

data L=N Int|C[L] 
d#C l=((C .pure.d)#)=<<l
d#n=[d n]
f(C l)=C$(id#)=<<l

Novamente, tenho que definir meu próprio tipo de lista, porque as listas de nativos de Haskell não podem ser aninhadas arbitrariamente. Esse novo tipo Lpode ser retornado de uma função, mas não ser impresso por padrão; portanto, para ver um resultado, defino uma showinstância para L:

instance Show L where
  show (N n)=show n
  show (C l)=show l

Agora podemos fazer alguns testes no REPL:

*Main> f $ C[N 1, C[N 2, N 3], C[N 4, N 4, C[N 5, N 2], N 1]]
[1,[2],[3],[4],[4],[[5]],[[2]],[1]]

*Main> f $ C[C[N 6, C[C[N 7]]]]
[[6],[[[7]]]]

Como funciona: uma recursão simples que passa o nível de aninhamento em função dos Cconstrutores. Começamos com a função de identidade ide sempre que houver uma lista (-> correspondência de padrão d#C l=), adicionamos uma camada adicional de C(-> C .pure.d) à chamada recursiva de #todos os elementos da lista. Se encontrarmos um número, simplesmente aplicamos a função de nível de aninhamento dao número.

nimi
fonte
0

APL (Dyalog) , 44 bytes *

Função de prefixo tácito anônimo. Toma a lista de APL aninhada como argumento e retorna uma matriz de APL aninhada.

∊{⊃⊂⍣⍵,⍺}¨{⊃¨(j∊⎕D)⊆+\-'[]'∘.=j←⎕JSON⍵}

Experimente online!

{} Aplique a seguinte função explícita em que o argumento é representado por :

⎕JSON⍵ converter o argumento para JSON

j← armazenar em j

'[]'∘.= tabela em que jé igual a colchetes abertos (linha superior) e fechados (linha inferior)

-⌿ linha superior menos linha inferior (redução da diferença vertical)

+\ soma cumulativa (isso fornece o nível de aninhamento para cada personagem)

()⊆ Partição, iniciando uma nova partição sempre que um 1 não é precedido por um 1 em…

  j∊⎕D onde cada caractere jé um membro do conjunto de D igits

⊃¨ escolha o primeiro de cada (isso fornece o nível de aninhamento por número de vários dígitos)

∊{...  aplicar a seguinte função para cada nvel de encadeamento ( ), usando o elemento correspondente do ε nlisted (achatada) argumento como argumento esquerda ( ):

,⍺ percorrer (listificar) o número (porque os escalares não podem ser incluídos)

⊂⍣⍵ incluir horários

 divulgar (porque a própria lista mais interna é um gabinete)


* Usando o Dyalog Classic com ⎕ML←3(padrão em muitos sistemas), substituindo por e para . Tio!

Adão
fonte