7
votes

Diviser la chaîne en tableau de chaînes de 1 à 3 mots dépend de la longueur

J'ai la chaîne d'entrée suivante

Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia ...

Règles de fractionnement par exemple

let s="Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia";

let a=[""];
s.split(' ').map(w=> {
  let line=a[a.length-1];
  let n= line=="" ? 0 : line.match(/ /g).length // num of words in line
  if(n<3) line+=w+' ';
  n++;
  if(n>=3) a[a.length-1]=line 
}); 

console.log(a);

Ainsi, comme vous pouvez le voir, la chaîne dans le tableau peut avoir un minimum et un maximum de mots d'arbre. J'essaye de le faire comme suit mais ça ne marche pas - comment faire?

[
     "Lorem ipsum dolor",  // A: Tree words <6 letters  
     "sit amet",           // B: Two words <6 letters if next word >6 letters
     "consectetur",        // C: One word >=6 letters if next word >=6 letters
     "adipiscing elit",    // D: Two words: first >=6, second <6 letters
     "sed doeiusmod",      // E: Two words: firs<6, second >=6 letters
     "tempor"              // rule C
     "incididunt ut"       // rule D
     "Duis aute irure"     // rule A
     "dolor in"            // rule B
     "reprehenderit in"    // rule D
     "esse cillum"         // rule E
     "dolor eu fugia"      // rule D
     ...
]

MISE À JOUR

Conditions aux limites: si les derniers mots / mot ne correspondent à aucune règle, ajoutez-les simplement comme dernier élément du tableau (mais deux mots longs ne peuvent pas être plus récents dans une chaîne)

RÉSUMÉ ET CONCLUSIONS INTÉRESSANTES

Nous obtenons 8 bonnes réponses à cette question, dans certaines d'entre elles, il y avait une discussion sur le code auto-descriptif (ou auto-explicable). Le code auto-descriptif est lorsque la personne qui n'a pas lu la question est capable de dire facilement ce que le code fait exactement après le premier regard. Malheureusement, l'une des réponses présente un tel code - cette question est donc un exemple qui montre que l'auto-description est probablement un mythe


1 commentaires

line.match(/ /g) ne correspondra jamais à rien car vous avez déjà divisé les s sur l'espace, donc il n'y a pas d'espaces.


9 Réponses :


4
votes

Une option consiste à créer d'abord un tableau de règles, comme:

const rules = [
      [3, 'less', 'less', 'less'],
      [2, 'less', 'less', 'eqmore'],
      [1, 'eqmore', 'eqmore'],
      [2, 'eqmore', 'less'],
      [2, 'less', 'eqmore'],
      [1]
    ];
const s = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia";

const words = s.split(' ');
const output = [];
const verify = (cond, word) => cond === 'less' ? word.length < 6 : word.length >= 6;
while (words.length) {
  const [wordCount] = rules.find(
    ([wordCount, ...conds]) => conds.every((cond, i) => words[i] && verify(cond, words[i]))
  );
  output.push(words.splice(0, wordCount).join(' '));
}
console.log(output);

Ensuite, parcourez le tableau de règles, trouvez la règle qui correspond, extrayez le nombre approprié de mots à épisser à partir de la règle de correspondance et poussez vers le tableau de sortie:

    const rules = [
      [3, 'less', 'less', 'less'],
      [2, 'less', 'less', 'eqmore'],
      [1, 'eqmore', 'eqmore'],
      [2, 'eqmore', 'less'],
      [2, 'less', 'eqmore']
    ];
const s = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia";

const words = s.split(' ');
const output = [];
const verify = (cond, word) => cond === 'less' ? word.length < 6 : word.length >= 6;
while (words.length) {
  const [wordCount] = rules.find(
    ([wordCount, ...conds]) => conds.every((cond, i) => verify(cond, words[i]))
  );
  output.push(words.splice(0, wordCount).join(' '));
}
console.log(output);

Bien sûr, le .find suppose que chaque chaîne d'entrée aura toujours une règle de correspondance pour chaque position épissée.

Pour la règle supplémentaire selon laquelle tous les mots ne correspondant pas aux règles précédentes doivent simplement être ajoutés à la sortie, mettez [1] au bas du tableau de rules :

const rules = [
  // [# of words to splice if all conditions met, condition for word1, condition for word2, condition for word3...]
  [3, 'less', 'less', 'less'],
  // the above means: splice 3 words if the next 3 words' lengths are <6, <6, <6
  [2, 'less', 'less', 'eqmore'],
  // the above means: splice 2 words if the next 3 words' lengths are <6, <6, >=6
  [1, 'eqmore', 'eqmore'],
  [2, 'eqmore', 'less'],
  [2, 'less', 'eqmore']
];


4 commentaires

pour s = "Lorem ipsum dolor sit" code lève une exception


Cette entrée ne correspond pas aux règles, et votre question ne spécifie pas ce qui devrait se passer dans un tel cas - comme je l'ai dit, le .find suppose que chaque chaîne d'entrée aura toujours une règle de correspondance pour chaque position épissée. Une fois que la règle A est mise en correspondance (3 caractères de <6 caractères), nous avons juste "assis", mais un seul mot de moins de 6 caractères ne correspond à aucune des règles.


si les derniers mots / mot ne correspondent à aucune règle (à la fin du tableau) et à aucune règle (par exemple, deux mots longs ne peuvent pas être plus récents dans une chaîne) alors ajoutez-le simplement comme dernier élément du tableau - je mets à jour la question


Voir edit, il suffit de mettre [1] au bas du tableau des règles, pour cette 6ème règle



5
votes

Vous pouvez exprimer vos règles sous forme d'expressions régulières abrégées, créer une vraie regex à partir d'elles et l'appliquer à votre entrée:

text = "Lorem ipsum, dolor. sit amet? consectetur,   adipiscing,  elit! sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia bla?";

rules = ['(SSS)', '(SS(?=L))', '(L(?=L))', '(SL)', '(LS)', '(.+)']

regex = new RegExp(
    rules
        .join('|')
        .replace(/S/g, '\\w{1,5}\\W+')
        .replace(/L/g, '\\w{6,}\\W+')
    , 'g')

console.log(text.match(regex))

Si les règles ne changent pas, la partie de construction regex n'est nécessaire qu'une seule fois.

Notez que cela gère également la ponctuation de manière raisonnable.


3 commentaires

Solution RegExp intéressante mais je confirme qu'avec le texte original il manque les 3 derniers mots - donc le résultat est faux. Regle-le, s'il te plait


Votre solution était agréable mais donne un mauvais résultat, je ne peux donc pas l'accepter. J'accepte la réponse de BoltKey qui donne aussi une solution très originale (il trouve un moyen alternatif d'exprimer des règles - comme 'somme' ...)


Afin de le faire fonctionner avec le texte d'origine, ajoutez la règle suivante (\\ w [^ \ s] *). Les vars manquent également sa déclaration.



1
votes

Cela ressemble à un problème que vous auriez lors d'un entretien d'embauche ou d'un test. La bonne façon d'aborder ce problème est de réfléchir à la manière de simplifier le problème en quelque chose que nous pouvons comprendre et pour lequel nous pouvons écrire du code lisible .

Nous savons qu'il y a deux conditions: inférieure à six ou pas . Nous pouvons représenter chaque mot de la chaîne comme un chiffre binaire de 0 ( plus petit que 6 ) ou 1 ( plus grand que 6 ).

Transformer la chaîne de mots en une chaîne de binaire facilitera le traitement et la compréhension:

const s = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia";

let b = s.split(' ').reduce((array, word) => {
  return array + (word.length >= 6 ? "1" : "0");
}, "");

//console.log(b);
let a = '';
while (b != "") {
  switch (0) {
    case b.indexOf('000'):
      b = b.substring(3);
      a += '3';
      break;
    case b.indexOf('10'):
      b = b.substring(2);
      a += '2';
      break;
    case b.indexOf('01'):
      b = b.substring(2);
      a += '2';
      break;
    case b.indexOf('001'):
      b = b.substring(2);
      a += '2';
      break;
    case b.indexOf('11'):
      b = b.substring(1);
      a += '1';
      break;
  }
}

console.log(a);
//Go through the string of multi-word lengths and turn the old string into separate strings. 

const acc = [];
words = s.split(' ');
for (let index in a) {
  acc.push(words.splice(0, a[index]).join(' '));
}
console.log(acc);

Ensuite, nous devons simplifier les règles. Chaque règle peut être considérée comme une chaîne de binaire (un ensemble de mots). Étant donné que certaines règles sont plus compliquées que d'autres, l'ajout du mot suivant auquel nous penserons dans le cadre de la chaîne:

  1. [0,0,0] -> 000
  2. [0,0,1] -> 001
  3. [1,1] -> 11
  4. [1,0] -> 10
  5. [0,1] -> 01

Pour une chaîne de nombres restante, la règle qui correspond au début sera le prochain ensemble de chaînes. C'est une opération logique assez simple:

const s = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia";

const b = s.split(' ').reduce((array, word) => {
  return array + (word.length >= 6 ? "1" : "0");
}, "");

console.log(b);

YAY! Nous avons réussi à convertir un problème complexe en quelque chose de facile à comprendre. Bien que ce ne soit pas la solution la plus courte, elle est très élégante et il y a encore place à l'amélioration sans sacrifier la lisibilité (par rapport à certaines autres solutions).

Cette manière de conceptualiser le problème ouvre des portes à plus de règles voire à des états plus complexes (0,1,2).


0 commentaires

3
votes

Si nous définissons des mots de longueur <6 pour avoir la taille 1 et> = 6 pour avoir la taille 2, nous pouvons réécrire les règles en "si le mot suivant ferait la taille totale de la ligne courante> = 4, commencez la ligne suivante".

function wordSize(word) {
  if (word.length < 6) 
    return 1;
  return 2;
}
let s = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusd tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia";
var result = [];
var words = s.split(" ");
var row = [];
for (var i = 0; i < words.length; ++i) {
  if (row.reduce((s, w) => s + wordSize(w), 0) + wordSize(words[i]) >= 4) {
    result.push(row);
    row = [];
  }
  row.push(words[i]);
}
result.push(row);
result = result.map(a => a.join(" "));
console.log(result);


2 commentaires

dans la sortie, il manque les derniers mots de l'arbre: dolor eu fugia - veuillez le réparer


Votre approche est très intelligente - je l'aime bien :) - J'écris plus de code `` compressé '' en utilisant votre idée ici



1
votes

Aucune astuce nécessaire . Ce code parcourt le tableau de mots, et vérifie les règles pour chaque séquence de 3. Les règles sont appliquées en essayant de faire moins de boucles et en créant moins d'objets intermédiaires possibles, ce qui se traduit par de bonnes performances et une bonne utilisation de la mémoire.

function apply_rules(stack, stack_i) {

    let small_word_cnt = 0;

    for(let i = 0; i<= 2; i++){

        //Not enough elements to trigger a rule
        if(!stack[stack_i+i]){
            return stack.slice(stack_i, stack.length);
        }

        //Increment the small word counter
        small_word_cnt += stack[stack_i+i].length < 6;

        //2 big words
        if(i== 1 && small_word_cnt == 0){
            return [stack[stack_i]];
        }

        //3 small words
        if(small_word_cnt == 3){
            return stack.slice(stack_i,stack_i+3);
        }
    }

    //mixed small and big words;
    return stack.slice(stack_i,stack_i+2);
}

function split_text(text) {
    const words = text.split(' '), results = [];
    let i = 0;

    while(i < words.length) {
        const chunk = apply_rules(words, i);
        i+= chunk.length;
        results.push(chunk.join(' '));
    }

    return results;
}

console.log(split_text("Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia"));


2 commentaires

Je mets à jour ma réponse - mais le code n'est toujours pas auto-expainable - même si j'introduis tous vos conseils, le code sera toujours non-auto-exapinalbe - alors où est le point de le changer? Et qu'en est-il de votre code - on ne sait toujours pas ce que fait le code et il faut des commentaires supplémentaires ... (vos commentaires dans le code ne sont pas suffisants pour comprendre ce que fait le code) - pouvez-vous améliorer votre code pour changer cette situation?


@ KamilKiełczewski: Une grande partie de mon travail consiste à examiner le code JS des autres. À mes yeux, c'est un code assez lisible. Cela n'indique pas clairement quel problème il essaie de résoudre; ce sont des méta-connaissances qui ne sont pas disponibles ici, mais c'est très clair comment cela se passe. C'est pourquoi il y a eu des plaintes concernant votre réponse; il n'offrait aucun indice sur le problème qu'il essayait de résoudre ni n'indiquait raisonnablement comment il tentait de le résoudre.



-1
votes

J'écris en bref et plus rapidement (en termes de complexité temporelle: je ne calcule pas la somme en réduisant à chaque itération de boucle) la version de l'idée proposée dans la réponse BoltKey (si vous voulez voter, faites-le sur sa réponse).

Idée principale

  • ws est la taille du mot où nous n'avons que deux valeurs 1 (mot court) et 2 (mot long)
  • s est la taille de ligne courante dans la boucle (nous itérons sur chaque taille de mot)
  • si la taille de la ligne actuelle plus la taille du mot suivant s + ws> 3, les règles ne sont pas respectées (et c'est l'IDÉE PRINCIPALE découverte par BoltKey)
  • si les règles ne sont PAS brisées, ajoutez un mot à la ligne l , et sa taille à la taille de la ligne s
  • si les règles ne sont pas respectées, ajoutez la ligne l au tableau de sortie r et nettoyez l et s
  • dans l'instruction return, nous gérons le cas de terminaison: ajoutez les derniers mots de la ligne l au résultat si l n'est pas vide

let s = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusd tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia";

function split(n,str) {
  let words= str.split(' '), s=0, l=[], r=[];
  
  words.forEach(w=>{ 
    let ws= w.length<n ? 1:2;
    if(s+ws>3) r.push(l.join(' ')), s=0, l=[];
    l.push(w), s+=ws;
  })

  return l.length ? r.concat(l.join(' ')) : r;
}

console.log( split(6,s) );


1 commentaires

@filipe J'en ai choisi une: pouvez-vous indiquer laquelle des réponses (à cette question) contient du code qui s'explique d'elle-même ? (signifie: la personne qui n'a pas lu la question pourra facilement dire ce que fait exactement le code après le premier regard)



2
votes

( Mis à jour pour incorporer la suggestion de l'utilisateur633183.)

J'ai trouvé cela un problème intéressant. Je voulais écrire une version plus générique immédiatement, et j'ai opté pour une version qui acceptait une liste de règles, chacune décrivant le nombre de mots qu'elle rassemblerait et un test pour chacun de ces mots. Donc, avec lt6 étant essentiellement (str) => str.length < 6 , la première règle (A) ressemblerait à ceci:

[4, lt5, lt5, lt5, lt5]

Ceci, il s'avère, est assez similaire à la solution de CertainPerformance; cette réponse utilise des chaînes pour représenter deux comportements différents; celui-ci utilise des fonctions réelles. Mais ils sont assez similaires. La mise en œuvre, cependant, est assez différente.

const allMatch = (fns, xs) =>
  fns.every ( (fn, i) =>  fn ( xs[i] ) )

const splitByRules = (rules) => {
  const run = 
    ( xs
    , res = []
    , [count] = rules .find 
        ( ([count, ...fns]) => 
          count <= xs .length 
          && allMatch (fns, xs)
        ) 
        || [1] // if no rules match, choose next word only
    ) => xs.length === 0
      ? res
      : run 
        ( xs .slice (count) 
        , res .concat ([xs .slice (0, count) ])
        )

  return (str) => 
    run (str .split (/\s+/) ) 
      .map (ss => ss .join (' '))
}

const shorterThan = (n) => (s) => 
  s .length < n

const atLeast = (n) => (s) =>
  s .length >= n

const lt6 = shorterThan (6)
const gte6 = atLeast (6)

const rules = [
// +------------- Number of words to select in next block 
// |        +--------- Functions to test againt each word
// |   _____|_____
// V  /           \
  [3, lt6, lt6, lt6],   // A
  [2, lt6, lt6, gte6],  // B
  [1, gte6, gte6],      // C
  [2, gte6, lt6],       // D
  [2, lt6, gte6],       // E
]

const words  = 'Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia ...';

console .log (
  splitByRules (rules) (words) 
)

Cela utilise une fonction récursive qui se termine lorsque la liste de mots restante est vide et recherche autrement la première règle qui correspond (avec, encore une fois comme CertainPerformance, une règle par défaut qui prend simplement le mot suivant) et sélectionne le nombre de mots correspondant, récurrent sur les mots restants.

Pour plus de simplicité, la fonction récursive accepte un tableau de mots et renvoie un tableau de tableaux de mots. Une fonction wrapper gère la conversion de ceux-ci vers et à partir de chaînes.

La seule autre fonction de substance ici est la fonction d'assistance allMatch . C'est essentiellement ([f1, f2, ... fn], [x1, x2, ..., xn, ...]) => f1(x1) && f2(x2) && ... && fn(xn) .

Bien sûr, le currying signifie que splitByRules (myRules) renvoie une fonction que vous pouvez stocker et exécuter sur différentes chaînes.

L'ordre des règles peut être important. Si deux règles peuvent se chevaucher, vous devez placer la correspondance préférée avant l'autre.


Cette généralité supplémentaire peut vous intéresser ou non, mais je pense que cette technique présente un avantage significatif: elle est beaucoup plus facile à modifier si les règles changent un jour. Supposons maintenant que vous souhaitiez également inclure quatre mots, s'ils contiennent tous moins de cinq caractères. Ensuite, nous écririons simplement const lt5 = shorterThan(5) et incluons la règle

[3, lt6, lt6, lt6],

au début de la liste.

Pour moi, c'est une grande victoire.


4 commentaires

Il y a quelques heures, je vous ai lu un commentaire ci-dessous ma réponse et j'ai apporté quelques corrections, mais même avant cela, je pense que mon code était beaucoup plus lisible que votre code (honnêtement, votre code est difficile à comprendre pour moi). Pouvez-vous changer votre code pour qu'il soit plus lisible?


@ KamilKiełczewski: Si vous avez des suggestions spécifiques, comme Felipe l'a fait sur votre réponse, je serais plus qu'heureux de les examiner. Il y a six fonctions dans ma réponse, la principale, splitByRules et la fonction d'assistance allMatch ainsi que quatre fonctions très simples utilisées pour tester des mots individuels. Il y a aussi la définition des règles utilisées pour la question principale du PO. Je suppose que tout le reste est suffisamment lisible: la définition de l'entrée, l'appel de la fonction principale et l'appel à console.log . Alors, quelle partie trouvez-vous la plus déroutante?


Scott, excellente réponse utilisant des machines minimales. Cela me surprend encore lorsque certains points communs apparaissent entre des réponses très différentes. Une petite amélioration serait allMatch = (fns, xs) => fns.every((f, i) => f(xs[i])) qui donne une sortie anticipée. Merci d'avoir partagé: D


@ 633183: Merci. C'est définitivement une amélioration. Mis à jour pour l'inclure.



3
votes

J'ai également trouvé ce problème très intéressant. Ceci est une réponse au format long qui montre le processus par lequel je suis arrivé au programme final. Il y a plusieurs blocs de code étiquetés sketch cours de route. J'espère que cette approche sera utile aux débutants dans un style fonctionnel.

En utilisant le module data.maybe , j'ai commencé avec -

formatSentence ("Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia ...")

// [ 'Lorem ipsum dolor'
// , 'sit amet'
// , 'consectetur'
// , 'adipiscing elit'
// , 'sed doeiusmod'
// , 'tempor'
// , 'incididunt ut'
// , 'Duis aute irure'
// , 'dolor in'
// , 'reprehenderit in'
// , 'esse cillum'
// , 'dolor eu fugia'
// , '...'
// ]

Ensuite, j'ai commencé à écrire certaines des règles ...

const identity = x =>
  x

const always = x =>
  _ => x

const apply = (f = identity) =>
  (args = []) => f (...args)

const dual = f =>
  [ x => Boolean (f (x))
  , x => ! Boolean (f (x))
  ]

Beaucoup trop compliqué et répétitif , ai-je pensé. En tant qu'auteur de ces fonctions, c'est mon travail de les faire fonctionner pour moi! J'ai donc recommencé cette fois à concevoir les règles pour faire le dur travail -

const { Just, Nothing } =
  require ("data.maybe")

const [ small, large ] =
  dual ((word = "") => word.length < 6)

const oneOf = (init, ...more) => x =>
  more.reduce((r, f) => r .orElse (_ => f(x)), init (x))

const rule = (guards = [], take = 0) =>
  ([ words = [], r = [] ]) =>
    guards .every ((g, i) => g (words[i]))
      ? Just
          ( [ words .slice (take)
            , [ ...r, words .slice (0, take) .join (" ") ]
            ]
          )
      : Nothing ()

Ces règles sont beaucoup plus simples. Ensuite, je voulais nettoyer un peu wordsToLines -

const formatSentence = (sentence = "") =>
  wordsToLines (sentence .split (" "))
    .getOrElse ([])

Dans notre esquisse initiale, les règles ont construit un objet {line, next} , mais une rule ordre supérieur signifie que nous pouvons cacher encore plus de complexité. Et l'aide oneOf facilite le déplacement de nos règles en ligne -

// final revision
const wordsToLines = (words = [], r = []) =>
  words.length === 0
    ? Just (r)
    : oneOf
        ( rule ([ small, small, small ], 3) // A
        , rule ([ small, small, large ], 2) // B
        , rule ([ large, large ], 1)        // C
        , rule ([ large, small ], 2)        // D
        , rule ([ small, large ], 2)        // E
        , rule ([ always (true) ], 1) // default
        )
        ([ words, r ])
        .chain (apply (wordsToLines))

Enfin, nous pouvons écrire notre fonction principale, formatSentence -

// sketch 4
const wordsToLines = (words = [], r = []) =>
  words.length === 0
    ? Just (r)
    : oneOf (ruleA, ruleB, ruleC, ruleD, ruleE, defaultRule)
        (words)
        .chain (({ line, next }) => 
          wordsToLines (next, [...r, line ])
        )

Les fils sont pour la plupart démêlés maintenant. Nous devons juste fournir les dépendances restantes -

// sketch 3
const rule = (guards = [], take = 0) =>
  // TODO: implement me...

const ruleA =
  rule
    ( [ small, small, small ] // pattern to match
    , 3                       // words to consume
    )

const ruleB =
  rule ([ small, small, large ], 2)

// ruleC, ruleD, ruleE, ...

const defaultRule =
  rule ([ always (true) ], 1)

Et quelques primitives fonctionnelles -

// sketch 2
const success = (line, next) =>
  Just ({ line, next })

const defaultRule = ([ line, ...next ]) =>
  success (line, next)

const ruleA = ([ a, b, c, ...more ]) =>
  small (a) && small (b) && small(c)
    ? success (line (a, b, c), more)
    : Nothing ()

const ruleB = ([ a, b, c, ...more ]) =>
  small (a) && small (b) && large (c)
    ? success (line (a, b), [c, ...more])
    : Nothing ()

// ...

Lançons le programme -

// sketch 1
const wordsToLines = (words = [], r = []) =>
  words.length === 0
    ? Just (r)
    : ruleA (words)
        .orElse (_ => ruleB (words))
        .orElse (_ => ruleC (words))
        .orElse (_ => ruleD (words))
        .orElse (_ => ruleE (words))
        .orElse (_ => defaultRule (words))
        .chain (({ line, next }) => 
          wordsToLines (next, [...r, line ])
        )

Consultez le programme complet sur repl.it et exécutez-le pour voir les résultats -


1 commentaires

C'est bien. J'adore voir le chevauchement entre les trois réponses pour la plupart différentes de vous, de moi et de @CertainPerformance.



1
votes

J'ai vu des solutions très intelligentes ici, merci à tous!

Cependant, je pense qu'il y a place ici pour une solution optimisée pour "l'auto-documentation". Notez que mon objectif était que - l'auto-documentation - donc cette solution n'est sûrement pas le code le plus court, ni le plus rapide, ni le moins gourmand en mémoire.

"use strict;"

console.log(splitTextIntoWordGroups("Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia"));

function splitTextIntoWordGroups(text) {
  const words = text.split(' ');
  const wordGroups = [];

  while(true) {
    if(next3WordsAreAllShorterThan6Chars(words)) {
      wordGroups.push(moveNext3WordsToWordGroup(words));
    }
    else if(next2WordsAreAllShorterThan6CharsAndSubsequentWordIsLongerThan6Chars(words)) {
      wordGroups.push(moveNext2WordsToWordGroup(words));
    }
    else if(nextWordIsLongerOrEqualThan6CharsAndSubsequentWordIsLongerOrEqualThan6Chars(words)) {
      wordGroups.push(moveNextWordToWordGroup(words));
    }
    else if(nextWordIsLongerOrEqualThan6CharsAndSubsequentWordIsShorterThan6Chars(words)) {
      wordGroups.push(moveNext2WordsToWordGroup(words));
    }
    else if(nextWordIsShorterThan6CharsAndSubsequentWordIsLongerOrEqualThan6Chars(words)) {
      wordGroups.push(moveNext2WordsToWordGroup(words));
    }
    else {
      let remainingWordGroup = moveRemainingWordsToWordGroup(words);
      if(remainingWordGroup) {
        wordGroups.push(remainingWordGroup);
      }
      break;
    }
  }
  return wordGroups;
}

function next3WordsAreAllShorterThan6Chars(words) {
  if(words.length < 3) return false;
  if(words[0].length < 6 && words[1].length < 6 && words[2].length < 6) return true;
  return false;
}

function next2WordsAreAllShorterThan6CharsAndSubsequentWordIsLongerThan6Chars(words) {
  if(words.length < 3) return false;
  if(words[0].length < 6 && words[1].length < 6 && words[2].length > 6) return true;
  return false;
}

function nextWordIsLongerOrEqualThan6CharsAndSubsequentWordIsLongerOrEqualThan6Chars(words) {
  if(words.length < 2) return false;
  if(words[0].length >= 6 && words[1].length >= 6) return true;
  return false;
}

function nextWordIsLongerOrEqualThan6CharsAndSubsequentWordIsShorterThan6Chars(words) {
  if(words.length < 2) return false;
  if(words[0].length >= 6 && words[1].length < 6) return true;
  return false;
}

function nextWordIsShorterThan6CharsAndSubsequentWordIsLongerOrEqualThan6Chars(words) {
  if(words.length < 2) return false;
  if(words[0].length < 6 && words[1].length >= 6) return true;
  return false;
}

function moveNext3WordsToWordGroup(words, results) {
  return moveNextNWordsToWordGroup(words, 3);
}

function moveNext2WordsToWordGroup(words, results) {
  return moveNextNWordsToWordGroup(words, 2);
}

function moveNextWordToWordGroup(words, results) {
  return moveNextNWordsToWordGroup(words, 1);
}

function moveNextNWordsToWordGroup(words, n) {
  wordGroup = [];
  for(let i=0; i < n; i++) {
    wordGroup.push(words.shift());
  }
  return wordGroup.join(' ');
}

function moveRemainingWordsToWordGroup(words) {
  if(words.length > 0) {
    wordGroup = [];
    wordGroup.push(...words);
    return wordGroup.join(' ');
  }
}


0 commentaires