Voglio rispondere a una piccola parte della tua domanda, che è comunque molto interessante:
Is it possible to use a fold (also known as reduce) operation for this?
Sì, lo è.
In effetti, è sempre possibile usare una piega per qualsiasi cosa ! Fold è un'operazione generale per l'iterazione, qualsiasi cosa puoi fare con l'iterazione, puoi farlo con fold!
La pagina di Wikipedia per la piega ha uno schizzo di prova per questo, ma io voglio dare un'altra intuizione grazie a un discorso di Rúnar Óli Bjarnason su Programmazione funzionale per principianti che sfortunatamente sembra essere svanita dall'interweb.
Una raccolta può essere vuota o non vuota. I due argomenti da piegare indicano cosa fare quando la raccolta è vuota e cosa fare con l'elemento successivo quando la raccolta non è vuota. Ciò copre tutti i casi, quindi dovrebbe essere intuitivamente chiaro che quella piega può fare qualsiasi cosa .
Un'altra vista, grazie a quello stesso discorso, è di piegare come interprete. Pensa a una collezione come a una serie di istruzioni. Esistono due tipi di istruzioni: l'istruzione HALT
(che non ha operandi) e l'istruzione NEXT
, che ha un operando. I due argomenti che fornisci per passare, sono le due implementazioni delle funzioni dell'interprete per HALT
e NEXT
. Di nuovo, dato che ci sono solo due istruzioni e tu fornisci il codice per entrambi, non sorprende che quella piega possa fare qualsiasi cosa.
In realtà l'ho messo alla prova e ha riutilizzato molti dei metodi dal framework di raccolta di Ruby (la Enumerable
module), che normalmente si basano only sul metodo each
(un foreach
-style iterator che restituisce semplicemente ogni elemento), in modo che invece si basino tutti sul metodo inject
( che è il modo in cui gli incantesimi di Ruby si "piegano").
Quindi, che fa ha l'aspetto di una piega in ECMAScript?
someArray.reduce((memo, el, i, ary) => memo && ary[0] === el, true)
Tuttavia, solo perché fold può fare tutto, non dovrebbe essere usato per tutto. Quando ci sono operazioni più appropriate e più specializzate, queste dovrebbero essere invece utilizzate:
someArray.every((el, i, ary) => ary[0] === el)
Nota: tecnicamente parlando, queste due implementazioni non controllano se ogni elemento è uguale a ogni altro elemento, controllano solo se ogni elemento è uguale al primo elemento. Quindi, si basa su ===
che è transitivo e simmetrico (come dovrebbe essere un'eguaglianza ben condotta). Se non puoi fare affidamento su questo, devi prima costruire il prodotto cartesiano dell'array con se stesso, mapparlo per stabilire se i due elementi sono uguali e poi chiedere se è tutto vero:
someArray.reduce((memo, el, _, ary) => memo.concat(ary.map(ell => [el, ell])), []).
map(([a, b]) => a === b).
every(el => el)