An Array of Challenges # 2: Separate a Nested Array


36

Nota: questo è il numero 2 in una serie di sfide di di . Per la sfida precedente, fai clic qui .

Separare gli elenchi nidificati

Per separare i valori in un elenco nidificato, appiattirlo e quindi avvolgere ciascun valore in modo che sia alla stessa profondità nidificata di prima.

Vale a dire, questo elenco:

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

Potrebbe diventare:

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

La sfida

Il tuo compito è quello di scrivere un programma che prende qualsiasi elenco nidificato di numeri interi positivi (entro i limiti della tua lingua) ed esegue questa operazione di separazione.

È possibile inviare una funzione che accetta l'elenco come argomento o un programma completo che esegue I / O.

Dato che si tratta di , vince l'invio più breve (in byte)! *

* Le scappatoie da golf standard sono vietate. Conosci il trapano.


Casi test

Gli elenchi di input conterranno sempre numeri interi nella dimensione intera standard della tua lingua. Per evitare i vincoli delle lingue che impediscono loro di competere, i valori non saranno nidificati a profondità superiori a 10.

Si può presumere che l'input non avrà sottoelenco vuoti: per esempio - [[5, []]]non verrà dato. Tuttavia, l'elenco principale potrebbe essere vuoto.

[]            ->  []

[[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]

Non esitate a lasciare un commento se ho perso un caso d'angolo.

Esempio

Ho gettato insieme un rapido (ungolfed) Python 3 soluzione come un esempio - è possibile testare sul repl.it .


Aggiungi una testcase con numeri maggiori di una cifra per le risposte basate su stringhe.
orlp

@orlp buona idea.
FlipTack,

2
Possiamo assumere una certa profondità massima? Di '16 anni?
orlp

@orlp Dirò di sì, la massima profondità nidificata sarà 10, poiché sono più interessato al tuo algoritmo e all'esecuzione del metodo rispetto ai vincoli del tuo linguaggio. Aggiornerà il thread ora.
FlipTack,

Posso emettere come stringa?
Rohan Jhunjhunwala,

Risposte:


4

Brachylog , 16 byte

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

Provalo online!

Spiegazione

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]

Cosa fa l' Zargomento su TIO? Senza di essa, questo sembra emettere con vero / falso, il che lo fa sembrare Znecessario nel conteggio dei byte.
FlipTack,

@FlipTack Zdice a Brachylog che l'argomento di output è una variabile. Questa è questa variabile che viene unificata con l'output risultante. Quando lo rimuovi, dice a Brachylog che l'output è una variabile anonima e invece stampa se il predicato principale ha esito positivo o negativo. Questo è lo stesso di Prolog dove il risultato è "messo" in una variabile.
Fatalizza il

Ok :) bella risposta!
FlipTack,

19

Matematica, 24 21 byte

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

o uno di questi:

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

Spiegazione

La ragione per cui è così breve è che è fondamentalmente una ricorsione che non richiede un caso base esplicito.

C'è un sacco di zucchero sintattico qui, quindi cominciamo con questo non golf. &denota una funzione senza nome rimasta, il cui argomento è scritto come #. All'interno di questa funzione si #0riferisce alla funzione stessa, che consente di scrivere funzioni ricorsive senza nome. Ma cominciamo dando un nome alla funzione interna e tirandola fuori:

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

L'altro importante zucchero sintattico f/@xè l'abbreviazione per Map[f, x]cui richiama fogni elemento di x. Il motivo f[x_] := ... f /@ xnon porta a una ricorsione infinita è che la mappatura di qualcosa su un atomo lascia invariato l'atomo senza effettivamente chiamare la funzione. Pertanto, non è necessario verificare esplicitamente il caso base (l'elemento corrente è un numero intero).

Quindi la funzione fprima ricade nell'elenco più profondo all'interno x, a quel punto f/@diventa una no-op. Quindi chiamiamo uso ##& @@ List /@su quello. La mappatura Listsull'elenco semplicemente avvolge ogni elemento in un elenco separato, così {1, 2, 3}diventa {{1}, {2}, {3}}. Quindi applichiamo ##& ad esso, il che significa che la testa (cioè l'elenco esterno) viene sostituita da ##&, quindi questo si trasforma in ##&[{1}, {2}, {3}]. Ma ##&restituisce semplicemente i suoi argomenti come aSequence (che puoi immaginare come un elenco da scartare o una sorta di operatore "splat" in altre lingue).

Quindi ##& @@ List /@trasforma un elenco {1, 2, 3}in {1}, {2}, {3}(tipo di, l'ultima cosa è effettivamente racchiusa nella testaSequence , ma questo svanisce non appena usiamo il valore ovunque).

Ciò lascia la domanda perché di per fsé non è già la soluzione alla sfida. Il problema è che l'elenco più esterno deve essere trattato in modo diverso. Se abbiamo input {{1, 2}, {3, 4}}che vogliamo {{1}, {2}, {3}, {4}}e non {{1}}, {{2}}, {{3}}, {{4}} . La mia soluzione originale ha risolto questo problema passando il risultato finale come un elenco di argomenti a Joincui ripristinare il livello esterno degli elenchi, ma questo salta semplicemente il livello esterno utilizzando f se stesso in una mappa sull'output. Quindi fviene applicato solo ai singoli elementi dell'elenco più esterno e non riesce mai a toccare tale elenco.

Per quanto riguarda le altre tre soluzioni, la prima applica semplicemente la ricorsione al di fuori della fquale funziona altrettanto bene. Le altre due soluzioni evitano un'operazione ripetuta Mapcomponendo prima due funzioni e quindi mappando il risultato una sola volta.


8

J , 19 18 byte

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

Questo è un verbo anonimo che accetta e restituisce array inscatolati, che sono la versione J (piuttosto ingombrante) degli array nidificati. Guardalo passare tutti i casi di test.

Spiegazione

Questo utilizza le operazioni in qualche modo esotiche {::( mappa ) e S:( diffusione ), che operano su array inscatolati. {::sostituisce ogni foglia con il percorso inscatolato di quella foglia. S:applica un determinato verbo a una determinata profondità di annidamento, quindi suddivide i risultati in un array.

(<@]/@,~>)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.

3

R, 199 byte

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})}

Questa domanda era DIFFICILE. Le liste di R sono un po 'strane e non è assolutamente semplice passare in rassegna tutti gli elementi delle liste. Inoltre, non è semplice determinare la profondità di tale elenco. Quindi la sfida diventa ricreare l'elenco con tutti gli elementi separati, quindi abbiamo anche bisogno di un modo per creare in modo adattivo un elenco di una certa profondità.

La soluzione è composta da due grandi parti. Una funzione ricorsiva che scorre su tutte le liste e registra la profondità:

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

Quando abbiamo la profondità di ogni voce del vettore unlist(l), memorizzata in d, creiamo implicitamente un elenco attraverso lapplye lo riempiamo con la seguente funzione:

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

In questa chiamata di applicazione, creiamo un oggetto qcon il valore della voce nell'elenco, ne controlliamo la profondità e vediamo se è diverso da zero. Se è zero, possiamo semplicemente lasciarlo come valore numerico. Se è diverso da zero, dobbiamo annidarlo in tale quantità di elenchi. Quindi chiamiamo un tempo for-loop de ripetutamente chiamiamo q=list(q).

lapplyquindi inserisce tutti questi valori qin un elenco, creando l'output desiderato.

Programma completo con spaziatura corretta e tale:

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)
}

Bello, questo è il metodo che ho usato con la mia soluzione Python iniziale per i casi di test :)
FlipTack

is.list(y)invece di class(y)=='list'? non posso verificare che funzionerà davvero.
Giuseppe,



2

C (gcc), 147 byte

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);}

Esempio di input:

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

Esempio di output:

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

2

impilato , non competitivo, 25 byte

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

Questa è una funzione in quanto modifica il membro superiore dello stack. Se vuoi una funzione autentica, aggiungi [e ]all'inizio e alla fine.Provalo qui!

Ecco una versione leggibile:

{ 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 di prova:

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

Uscita senza newline:

(1 (2) (3) (4) (4) ((5)) ((2)) (1))

È *come argomento per bloccare il codice?
Downgoat,

@Downgoat in questo caso avvolge i d-1tempi dell'argomento . $funcè una funzione che può essere manipolata.
Conor O'Brien,

2

PHP, 101 94 byte

salvato 1 byte grazie a @Christoph, salvato altri 6 ispirati da quello.

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

funzione ricorsiva, piuttosto semplice

abbattersi

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
}

Dove viene inizializzato il risultato?
Neil,

@Neil: PHP non richiede l'inizializzazione esplicita. Sia $rottiene elementi nel loop o la funzione restituisce un array vuoto. Potrebbe generare avvisi, ma quelli non vengono stampati con la configurazione predefinita.
Tito

Ciò non significa che saresti in grado di chiamarlo solo una volta?
Neil,

1
Si potrebbe anche impazzire: !cos(). cos()ritorna nullper ogni array e un float! = 0 per ogni numero intero positivo. Voglio dire ... chi se ne frega degli avvertimenti?
Christoph,

1
@Christoph: gli avvisi vengono stampati, gli avvisi no (nella configurazione predefinita). Ma è un'ottima idea! On is_int: Invertire la condizione non salva nulla; Ho bisogno di uno spazio tra elsee foreach. MA: $b[0]per un numero intero è NULL.
Tito

2

Python 2, 122 106 byte

Punteggio piuttosto terribile, solo un'implementazione semplice.

Grazie a @Zachary T per aver contribuito a salvare 16 byte!

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)

Chiama xcon un argomento da eseguire. Per qualche motivo può essere eseguito una sola volta.


È possibile passare a+=[n(l,d)]a a+=n(l,d),(notare la virgola finale)
FlipTack

Devi anche assegnare a t?
Zacharý,

Funziona quando lo chiami più di una volta?
Zacharý,

È possibile passare nalla funzione e rimuovere il primo argomento poiché sarà sempre l.
Zacharý,


2

JavaScript (Firefox 30-57), 53 byte

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

La migliore risposta ES6 che ho finora è di 76 byte:

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

2
In entrambi i blocchi di codice, penso che tu abbia omesso il comando f=.
Conor O'Brien,

@ ConorO'Brien Ancora una volta ...
Neil


1

Perl 6 , 60 47 byte

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

( Provalo online. )

Spiegazione:

  1. [... for |$^a]: Iterate sull'array di input e costruisce un nuovo array da esso.
  2. $_ ~~ List ?? ... !! ...: Per ogni elemento, controlla se è esso stesso un array.
  3. |([$_] for .&f): Se l'elemento è un array, applica ricorsivamente la funzione ad esso, scorre gli elementi del nuovo array restituito da quella chiamata ricorsiva, avvolgi ciascun elemento in un array a sé stante e inseriscili nell'elenco esterno.
  4. $_: Se l'elemento non è un array, passalo così com'è.

1

Haskell, 71 byte

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

Ancora una volta devo definire il mio tipo di elenco, perché gli elenchi di nativi di Haskell non possono essere nidificati arbitrariamente. Questo nuovo tipo Lpuò essere restituito da una funzione ma non può essere stampato per impostazione predefinita, quindi per vedere un risultato definisco showun'istanza per L:

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

Ora possiamo fare alcuni test nel 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]]]]

Come funziona: una semplice ricorsione che supera il livello di annidamento in funzione dei Ccostruttori. Iniziamo con la funzione identità ide ogni volta che c'è un elenco (-> pattern match d#C l=) aggiungiamo un ulteriore livello di C(-> C .pure.d) alla chiamata ricorsiva di #tutti gli elementi dell'elenco. Se incontriamo un numero, applichiamo semplicemente la funzione da livello di annidamento al numero.


0

APL (Dyalog) , 44 byte *

Funzione prefisso tacito anonimo. Prende l'elenco APL nidificato come argomento e restituisce un array APL nidificato.

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

Provalo online!

{... } applica la seguente funzione esplicita in cui l'argomento è rappresentato da :

⎕JSON⍵ converti l'argomento in JSON

j← conservare in j

'[]'∘.= tabella dove sono juguali le parentesi aperte (riga superiore) e chiuse (riga inferiore)

-⌿ riga superiore meno riga inferiore (riduzione della differenza verticale)

+\ somma cumulativa (questo fornisce il livello di nidificazione per ogni personaggio)

(... )⊆ partizione, iniziando una nuova partizione ogni volta che un 1 non è preceduto da un 1 in ...

  j∊⎕D dove ogni personaggio di jè un membro dell'insieme di D igits

⊃¨ scegli il primo di ciascuno (questo fornisce il livello di annidamento per numero a più cifre)

∊{...  applicare la seguente funzione per ogni livello di annidamento ( ), usando l'elemento corrispondente dal ε nlisted (appiattito) argomento come argomento a sinistra ( ):

,⍺ ravel (listify) il numero (perché gli scalari non possono essere racchiusi)

⊂⍣⍵ racchiudere i tempi

 divulgare (perché l'elenco più interno è esso stesso un allegato)


* Uso di Dyalog Classic con ⎕ML←3(impostazione predefinita su molti sistemi), sostituendo per e per . Tio!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.