Jam n'ajoute pas comme ça

16

Contexte

Les atomes arithmétiques de Jelly se vectorisent automatiquement. En fait, x + y est bien défini chaque fois que x et y sont des nombres ou des tableaux irréguliers de nombres. Le code source de Jelly implémente ce comportement à l'aide d'un vectoriseur générique, mais pour ce défi, nous ne considérerons que l'ajout d'entiers et de tableaux d'entiers imbriqués.

Définitions

Définissez la profondeur de x comme 0 si x est un entier, comme 1 s'il s'agit d'un tableau plat (éventuellement vide) d'entiers, et comme n + 1 s'il contient au moins un élément de profondeur n et aucun élément de profondeur k> n .

De cette façon, 1 a la profondeur 0 , [] et [1] et [1, 1] ont la profondeur 1 , [[], []] et [[1], [1]] et [[1]] et [1 , []] a la profondeur 2 , [1, [1, [1]]] a la profondeur 3 , etc.

L'opération x + y est définie comme suit.

  1. Si x et y ont une profondeur de 0 , renvoyez leur somme.

  2. Si x et y ont des profondeurs égales mais positives, appliquer récursivement + à tous les éléments de x et aux éléments correspondants de y .

    Si x et y ont des longueurs différentes, ajoutez la queue du tableau le plus long au tableau des sommes.

    Renvoie le résultat.

  3. Si la profondeur de x est strictement inférieure à la profondeur de y , appliquez récursivement + à x et à tous les éléments de y , et retournez le résultat.

    Faire le contraire si y « s profondeur est strictement inférieure à x » s.

Par exemple, considérons l'opération [1, [2, 3], [4]] + [[[10, 20], [30], 40, 50], 60] .

  • La profondeur de l'argument de gauche est 2 , tandis que la profondeur de l'argument de droite est 3 , donc nous calculons [1, [2, 3], [4]] + [[10, 20], [30], 40, 50 ] et [1, [2, 3], [4]] + 60 .

    • [1, [2, 3], [4]] et [[10, 20], [30], 40, 50] ont tous deux la profondeur 2 , nous calculons donc 1 + [10, 20] , [2, 3] + [30] et [4] + 40 .

      • 1 + [10, 20] = [1 + 10, 1 + 20] = [11, 21]

      • [2, 3] + [30] = [2 + 30, 3] = [32, 3]

        Notez que 3 reste intact, car il n'a pas d'élément correspondant.

      • [4] + 40 = [4 + 40] = [44]


      50 n'a pas un élément d'adaptation, de sorte que le résultat est [[[11, 21], [32, 3], [44], 50]] .

    • [1, [2, 3], [4]] + 60 = [1 + 60, [2, 3] + 60, [4] + 60] = [61, [2 + 60, 3 + 60], [ 4 + 60]] , ce qui donne [61, [62, 63], [64]] .

  • Le résultat final est [[[11, 21], [32, 3], [44], 50], [61, [62, 63], [64]]] .

Tâche

Écrivez un programme ou une fonction qui prend en entrée deux entiers, deux tableaux imbriqués d'entiers ou une combinaison de ceux-ci et renvoie leur somme, comme défini ci-dessus.

Si votre langue a plusieurs types de tableaux (listes, tuples, vecteurs, etc.), vous pouvez choisir l'un d'eux pour votre réponse. Le type de retour doit correspondre au type d'argument.

Pour éviter des solutions ennuyeuses et imbattables, si une langue a cette opération exacte en tant que intégré, vous ne pouvez pas utiliser cette langue.

Toutes les intégrations de toutes les autres langues sont autorisées. Si la langue de votre choix le permet, vous pouvez surcharger et / ou redéfinir l'ajout intégré.

Il s'agit de , donc le code le plus court en octets l'emporte.

Cas de test

0 + 0                           = 0
[-1, 0, -1] + [1]               = [0, 0, -1]
[] + [0]                        = [0]
[] + 0                          = []
[] + []                         = []
[[], 0] + []                    = [[], []]
[1, 2, 3] + 10                  = [11, 12, 13]
[1, 2, 3] + [10]                = [11, 2, 3]
[1, 2, 3] + [10, [20]]          = [[11, 12, 13], [21, 2, 3]]
[1, 2, 3, []] + [10, [20]]      = [11, [22], 3, []]
[1, [2, [3, [4]]]] + [10, [20]] = [[11, [21]], [[12, [22]], [13, [24]]]]

Pour générer plus de cas de test, vous pouvez utiliser ce programme Jelly .

Dennis
la source
Que faire si notre langue ne prend pas en charge les tableaux irréguliers? Sommes-nous autorisés à restructurer l'entrée ou devons-nous implémenter des tableaux irréguliers? Ou peut-être simplement utiliser une langue différente?
miles
Qu'entendez-vous par restructuration de l'entrée ?
Dennis
En y réfléchissant davantage, je me rends compte que cela ne fonctionnerait pas pour restructurer l'entrée mais de toute façon je vais résumer ce que je voulais dire auparavant. J'ai pensé à utiliser une valeur de remplissage pour remplir, ce qui éliminerait du travail mais créerait également un problème différent (probablement différent de la question que vous vouliez), mais réalisez maintenant que vos cas de test incluent également des nombres négatifs.
miles
Les tableaux peuvent également être hétérogènes, donc les valeurs de remplissage ne suffiraient pas à les rendre rectangulaires. En dernier recours, il y a toujours la possibilité de fonctionner sur des chaînes, mais c'est probablement trop compliqué.
Dennis
3
Hé, joli titre! .. maintenant que Google m'a aidé à l'obtenir :-)
Luis Mendo

Réponses:

3

Pyth, 42 octets

L?sIb0heSyM+b0M?qFyMJ,GH?yGgM.tJ0+GHgLFyDJ

Suite de tests

Les 4 derniers octets exécutent simplement la fonction sur l'entrée.

L?sIb0heSyM+b0M?qFyMJ,GH?yGgM.tJ0+GHgLFyDJ

L?sIb0heSyM+b0
                  Define y(b), a helper function to calculate the depth.
 ?                Ternary:
  sIb             If b is invariant under the s function, which is only the case
                  if s is an int.
     0            The depth is 0.
           +b0    Add a 0 on to b. This handles the edge case where b is [].
         yM       Map each to their depth
       eS         Take the max.
      h           Add one.

M?qFyMJ,GH?yGgM.tJ0+GHgLFyDJ
M                               Define g(G, H), which calculates the Jelly +.
 ?                              Ternary:
       ,GH                      Form [G, H].
      J                         Save it to J.
    yM                          Map each to its depth.
  qF                            Check if they are equal.
          ?yG                   If so, check if the depth is nonzero.
               .tJ0             If so, transpose J, pairing each element of each
                                argument with the corresponding element of the
                                other. Pad with zeroes.
             gM                 Map each to its Jelly +.
                   +GH          If the depths are zero, return the normal sum.
                         yDJ    If the depths are different, order J by depth.
                      gLF       Apply the function which left-maps the Jelly +
                                function to the two values. The first is
                                treated as a constant, while the second varies
                                over elements over the second values.
isaacg
la source
7

APL, 44 octets

{1=≡⍺⍵:⍺+⍵⋄=/∆←|≡¨⍺⍵:⊃∇¨/↓↑⍺⍵⋄</∆:⍺∘∇¨⍵⋄⍵∇⍺}

APL +distribue également sur des tableaux, mais d'une manière suffisamment différente pour que cela ne puisse pas vraiment être utilisé. Cependant, il existe une fonction de profondeur intégrée ( ).

Explication:

  • 1=≡⍺⍵:⍺+⍵: si les profondeurs de sont toutes deux nulles (et donc la profondeur de ⍺ ⍵est 1), ajoutez-les.
  • ∆←|≡¨⍺⍵: Prendre l'absolu de la profondeur à la fois et et les stocker dans . ( donne une valeur négative si tous les éléments n'ont pas la même profondeur.)
  • =/∆: s'ils ont la même profondeur:
    • ↓↑⍺⍵: remplir le tableau le plus court avec des zéros pour correspondre au tableau le plus long
    • ⊃∇¨/: distribuer la fonction sur les deux tableaux
  • </∆: si la profondeur de est inférieure à celle de :
    • ⍺∘∇¨⍵: lier puis cartographier
  • ⍵∇⍺: si rien d'autre (donc c'est plus profond que ), échangez les arguments et réessayez.
marinus
la source
3
Parfois, je pense que je connais bien l'APL. Ensuite, je vois un chef-d'œuvre comme celui-ci et je me rends compte que je le connais à peine.
Alex A.
Les caractères APL comptent-ils vraiment comme des octets?
metalim
@metalim APL possède des pages de codes héritées qui ont précédé Unicode de quelques décennies. Dans ceux-ci, chaque caractère est un seul octet.
Dennis
Ensuite, le type de codage doit être fourni avec la solution. Juste IMO.
metalim
@metalim J'ai ajouté un lien.
Adám
5

Mathematica, 122 octets

d=Depth
x_~f~y_/;d@x>d@y:=y~f~x
x_~f~y_/;d@x<d@y:=x~f~#&/@y
x_List~f~y_:=MapThread[f,{x,y}~PadRight~Automatic]
x_~f~y_=x+y

Définit une fonction récursive fqui calcule la somme. Utilisant la correspondance de motifs de Mathematica, cette fonction se compose de quatre définitions distinctes:

x_~f~y_/;d@x>d@y:=y~f~x

Si la profondeur de xest supérieure à celle dey , permutez les arguments de sorte que nous n'ayons à gérer la distribution que dans une seule direction (ce que nous pouvons faire, car l'addition est commutative).

x_~f~y_/;d@x<d@y:=x~f~#&/@y

Si la profondeur de xest inférieure à celle de y, remplacez chaque valeur #par ypar f[x,#], qui s'occupe de la distribution des arguments de profondeur inégale.

x_List~f~y_:=MapThread[f,{x,y}~PadRight~Automatic]

Sinon, si un argument est une liste (ce qui implique que l'autre est également une liste, car nous savons qu'ils ont la même profondeur), nous mettons les deux arguments dans une liste, les PadRight[..., Automatic]remplissons à la même longueur avec (qui remplit simplement un tableau irrégulier avec des zéros pour le rendre rectangulaire), puis utilisezMapThread pour appliquer faux paires correspondantes des deux listes.

Et enfin, le cas de base:

x_~f~y_=x+y

Si aucun des autres modèles ne correspond, nous devons essayer d'ajouter deux nombres, alors nous le faisons simplement.

Martin Ender
la source
5

Haskell, 150 octets

data L=S Int|V{v::[L]}
d(V z)=1+maximum(d<$>S 0:z);d _=0
S x!S y=S$x+y
x!y|d x<d y=V$(x!)<$>v y|d x>d y=y!x|1<2=V$v x#v y
(x:a)#(y:b)=x!y:a#b;a#b=a++b

Explication

La première ligne définit un type de données algébrique L, qui est soit un Scalar (contenant un Int) ou un Vector (contenant une liste de Ls, accessible à l'aide d'un getter d'enregistrement v, qui est une fonction partielleL → [L] .)

La deuxième ligne définit la fonction de profondeur : la profondeur d'un Vecteur est une plus sa profondeur maximale. Je fais précéder S 0les valeurs du vecteur, de sorte que depth [] == 1 + maximum [depth (S 0)] == 1. La profondeur de «toute autre chose» (un scalaire) est 0.

La troisième ligne définit le cas de base pour !(la fonction d'addition): la somme des scalaires est simplement un scalaire.

La cinquième ligne définit une variante zipWith (!)qui sélectionne simplement les éléments de la liste la plus longue lorsque l'un d'eux est vide.

La quatrième ligne est divisée en trois cas:

x!y | d x<d y = V$(x!)<$>v y
    | d x>d y = y!x
    | True    = V$v x#v y
  • Si la profondeur de xest strictement inférieure à la profondeur de y, cartographiez (x!)les éléments de y. (L'utilisation de vest garantie valide, as d(y) ≥ 1.)

  • Si la profondeur de xest strictement supérieure, retournez les arguments et redémarrez.

  • Si leurs profondeurs sont égales, zippez les arguments avec (!). (L'utilisation de vest garantie, car le cas a d(x) = d(y) = 0été traité comme un cas de base.)

Cas de test

instance Show L where
  show (S x) = show x
  show (V x) = show x

lArg = V [S 1, V [S 2, V [S 3, V [S 4]]]]
rArg = V [S 10, V [S 20]]

Alors show (lArg ! rArg) == "[[11,[21]],[[12,[22]],[13,[24]]]]".

Lynn
la source
J'ai juste corrigé cela aussi ^^ (j'avais changé les lignes pour plus de lisibilité, mais je l'ai fait de la mauvaise façon…) C'est importparce que Ideone a un vieux compilateur Haskell. Les versions modernes de GHC mis <$>en Prelude, afin que vous n'avez pas besoin d'importer Control.Applicativede l' utiliser ces jours -ci .
Lynn
Trop de modifications en même temps que mes autres actions: P Et bien sûr, cela semble correct maintenant, mais je trouve assez bizarre que cela provoque une erreur de compilation. Tous les bits de correspondance de motifs d'une fonction doivent-ils être consécutifs?
FryAmTheEggman
C'est exactement ça.
Lynn
D'accord, merci pour toute votre aide :) "Je vais comprendre un jour cette langue" - FryAmTheEggman il y a 7 ans.
FryAmTheEggman
4

Java, 802 794 754 746 octets

J'ai décidé de relever @ Dennis ♦ pour le défi d'opérer sur les cordes "en dernier recours" car c'était probablement "trop ​​compliqué". Aussi, dans la pire langue pour jouer au golf.

Les tableaux en entrée sont séparés par des virgules, entourés de crochets et sans espaces.

Programme complet avec des fonctions enveloppées dans une classe et avec des cas de test

import java.util.*;
List<String>p(String s){List r=new ArrayList<String>();String p="";int l=0;for(char c:s.substring(1,s.length()-1).toCharArray()){l+=c=='['?1:c==']'?-1:0;if(c==','&&l<1){r.add(p);p="";}else p+=c;}if(p!="")r.add(p);return r;}
int d(String s){int l=0;if(s.contains("[")){for(String c:p(s))l=d(c)>l?d(c):l;l++;}return l;}
String f(String x,String y){int i=0;String r="";if(d(x)<1&&d(y)<1)r+=Integer.valueOf(x)+Integer.valueOf(y);else{r="[";if(d(x)<d(y))for(String k:p(y))r+=(i++<1?"":",")+f(x,k);else if(d(x)>d(y))for(String k:p(x))r+=(i++<1?"":",")+f(k,y);else for(;i<p(x).size()||i<p(y).size();i++)r+=(i<1?"":",")+(i<p(x).size()&&i<p(y).size()?f(p(x).get(i),p(y).get(i)):i<p(x).size()?p(x).get(i):p(y).get(i));r+="]";}return r;}

Je pourrais porter cela en C ++ plus tard, car c'est l'autre langage que je connais qui ne prend pas en charge les tableaux irréguliers, car je suis presque sûr que ce sera plus court que cette réponse. C'était surtout une preuve de concept mais tous les conseils de golf seraient toujours appréciés!

-31 octets de @ user902383 suggérant d'utiliser un foreach sur un tableau de caractères converti, puis j'ai économisé un peu plus en réorganisant les blocs if dans la partie finale.

Encre de valeur
la source
C'est impressionnant.
Dennis
Je pense que si vous remplacez vos boucles par un tableau de chars de boucle foreach obtenu à partir d'une chaîne, vous pourriez économiser beaucoup d'octets.
user902383
1
Errr ... Java prend en charge les tableaux irréguliers; Je ne sais pas ce que tu veux dire par là. Utilisez Object[], qui contient soit imbriqué, Object[]soit Integer. Ou simplement une liste non générique.
Robert Fraser
4

Python 2.7, 261 209 202 198 191 191 185 197 181 octets

Solution triviale FGITW

EDIT: Bien sûr, @Dennis le bat

Merci à @LeakyNun pour avoir économisé 57 octets avec des conseils sur les expressions lambda et 2 octets à partir de crochets inutiles.

Merci à @Adnan pour 4 octets en raison de la suggestion d'utiliser typeau lieu deisinstance

Merci à @Lynn pour 7 octets avec -~etmap

Merci à @FryAmTheEggman pour z>=[]au lieu detype

+12 octets pour convertir lambda en if else et corriger un bug majeur

-16 octets grâce à @Kevin Lau - pas Kenny

Essayez-le en ligne

d=lambda z:z==[]or z>[]and-~max(map(d,z))
p=lambda x,y:p(y,x)if d(x)>d(y)else(x+y if d(x)<1 else[p(a,b)for a,b in zip(x,y)]+x[len(y):]+y[len(x):])if d(x)==d(y)else[p(a,x)for a in y]
Bleu
la source
Il est encore plus court de passer à Python 2.7 et d'écrirez==[]or`z`>']'and ...
Lynn
En outre, je pense que le remplacement max(d(a)+1for a in z)par -~max(d(a)for a in z)enregistre un octet (car vous pouvez supprimer l'espace avant max). Ce qui est alors juste -~max(map(d,z)).
Lynn
Passer à python 2 économise encore plus en ce que vous pouvez vous changer [p(a,b)for a,b in zip(x,y)]en map(p,x,y). Vous pouvez toujours le faire en 3, mais vous devez ajouter un appel à list. Je pense que vous pourriez également améliorer la suggestion de Lynn z>=[]. Indépendant, vous devriez également pouvoir échanger l' typeordre de comparaison pour économiser de l'espace.
FryAmTheEggman
Euh, je voulais dire or`z`>'[', bien sûr, mais je ne peux plus changer mon commentaire. Mais en effet, z>[]c'est encore plus court (l' ==affaire est déjà traitée)!
Lynn
La carte @FryAmTheEggman ne fonctionne pas lorsque les listes sont de tailles différentes; zip tronqué correctement. Je
Blue
3

Python 2, 145 136 octets

d=lambda t:t>{}and-~max(map(d,t+[0]))
s=lambda x,y:s(y,x)if d(y)<d(x)else map(s,(x,[x]*len(y))[d(x)<d(y)],y)if d(y)else(x or 0)+(y or 0)

Testez-le sur Ideone .

Comment ça fonctionne

En Python 2, tous les entiers sont inférieurs à tous les dictionnaires, mais toutes les listes sont supérieures. d calcule récursivement la profondeur de t en retournant 0 pour les entiers ou le maximum incrémenté des profondeurs de ses éléments et 0 . t+[0]évite de placer une casse spéciale dans la liste vide.

s calcule récursivement la somme de Jelly de x et y .

Si y 'la profondeur dépasse x ' s, des s(y,x)appels s avec des arguments échangés, en vous assurant que d (x) ≤ d (y) .

Si y a une profondeur positive, map(s,(x,[x]*len(y))[d(x)<d(y)],y)procédez comme suit.

  • Si les profondeurs x et y correspondent, il s'exécute map(s,x,y), mappant s sur tous les éléments de x et les éléments correspondants de y .

    Dans le cas de listes de longueurs différentes, map passera None comme argument gauche ou droit pour les éléments manquants dans la liste plus courte.

  • Si la profondeur de x est inférieure à celle de y , elle s'exécute map(s,[x]*len(y),y), mappant s (x, ·) sur y .

Si y (et, par conséquent, x ) a la profondeur 0 , (x or 0)+(y or 0)remplace les arguments falsifiés ( Aucun ou 0 ) par des zéros et renvoie la somme des entiers résultants.

Dennis
la source
1

JavaScript (ES6), 152 octets

f=(a,b,g=a=>a.map?1+Math.max(0,...a.map(g)):0)=>g(a)<g(b)?f(b,a):g(b)<g(a)?a.map(e=>f(e,b)):g(a)?a.length<b.length?f(b,a):a.map((e,i)=>f(e,b[i]||0)):a+b
;t=(x,y,z)=>o.textContent+=`
${JSON.stringify(x)}
${JSON.stringify(y)}
${JSON.stringify(z)}
${JSON.stringify(f(x,y))}
`;`
0 + 0                           = 0
[-1, 0, -1] + [1]               = [0, 0, -1]
[] + [0]                        = [0]
[] + 0                          = []
[] + []                         = []
[[], 0] + []                    = [[], []]
[1, 2, 3] + 10                  = [11, 12, 13]
[1, 2, 3] + [10]                = [11, 2, 3]
[1, 2, 3] + [10, [20]]          = [[11, 12, 13], [21, 2, 3]]
[1, 2, 3, []] + [10, [20]]      = [11, [22], 3, []]
[1, [2, [3, [4]]]] + [10, [20]] = [[11, [21]], [[12, [22]], [13, [24]]]]`.slice(1).split`
`.map(l=>t(...l.split(/ [+=] /).map(a=>JSON.parse(a))));
<pre id=o></pre>

Neil
la source
1

Ruby 2.3, 143 145 148 149 149 octets

Ruby a toutes ces petites bizarreries dans la façon dont zipfonctionne avec des tableaux de différentes longueurs et mapavec des fonctions multi-arguments, ce qui en fait assez amusant pour jouer au golf.

f=->x,y{d=->a{-~(a.map(&d).max||0)rescue 0}
d[x]<d[y]?y.map{|e|f[x,e]}:d[x]>d[y]?x.map{|e|f[e,y]}:d[x]<1?x+(y||0):[*x.zip(y).map(&f),*y[x.size..-1]]}
Encre de valeur
la source
C'est très intéressant - je n'ai jamais vu cette erreur auparavant pour cette fonction. J'ai encore corrigé certaines choses à cause d'autres bogues maintenant, mais à part ça, cela fonctionne pour moi (mais échoue toujours sur ideone). Je pense que c'est parce que ideone exécute 2.1 et que j'ai 2.3, donc peut-être que 2.1 ne peut pas se contenter mapd'une fonction à deux arguments comme je l'ai configuré à la fin. Voici une version éditée pour 2.1 qui fonctionne qui ajuste l' mapappel à la fin pour fonctionner. ideone.com/q1jqTA
Encre
1

Julia, 113 octets

~=endof;!t=0t!=0&&1+maximum(!,[t;0])
x::Array+y::Array=(!x,~x)>(!y,~y)?y+x:!x<!y?map(t->x+t,y):~x<~y?[x;0]+y:x.+y

Essayez-le en ligne!

Comment ça fonctionne

~=endof

crée un alias de 1 octet pour endof , qui renvoie la longueur d'un tableau.

!t=0t!=0&&1+maximum(!,[t;0])

définit une fonction de profondeur. La profondeur de t est nulle si et seulement si 0t == 0 . Sinon, t est un tableau et sa profondeur est calculée comme le maximum incrémenté des profondeurs de ses éléments et 0 .[t;0]ajoute un 0 au tableau t , évitant ainsi la nécessité de mettre en cas particulier le tableau vide.

La somme intégrée de Julia + se comporte déjà comme la somme de Jelly si l'un (ou les deux) de ses arguments est un entier. Cependant, la somme de deux tableaux ( + ) nécessite des tableaux de même forme, et même la somme vectorisée ( . + ) Nécessite des tableaux pouvant être diffusés vers une forme commune.

Nous redéfinissons + pour une paire de tableaux via

x::Array+y::Array=(!x,~x)>(!y,~y)?y+x:!x<!y?map(t->x+t,y):~x<~y?[x;0]+y:x.+y

Cela n'affecte pas la définition de + pour les arguments entier / entier, tableau / entier ou entier / tableau.

(!x,~x)>(!y,~y)compare lexicographiquement les paires de profondeurs et de longueurs de x et y . Si la profondeur de x dépasse y , ou si leur profondeur correspond et la longueur de x dépasse y ,y+x appelle récursivement + avec des arguments échangés.

Sinon, !x<!yteste si la profondeur de x est inférieure à celle de y . Si c'est,map(t->x+t,y) mappe x + · sur y .

Si les profondeurs correspondent, ~x<~yteste si x est plus court que y . Si c'est le cas, [x;0]+yappelle récursivement + après avoir ajouté un 0 à l'argument de gauche.

Enfin, si les profondeurs et les longueurs sont identiques, x.+ymappe + sur tous les éléments de x et les éléments correspondants de y .

Dennis
la source