C ++ имеет несколько ограничений, которых нет в JavaScript. Существует бесчисленное множество способов кодирования вашей функции в JavaScript, но вот тот, который использует стиль передачи с продолжением .
В этом стиле второй параметр send
добавляется к подписи функции genStrings
и принимает продолжение по умолчанию identity
. Это эффективно превращает return
в настраиваемую пользователем функцию. Имя send
используется, потому что return
является зарезервированным ключевым словом.
Другим примечательным аспектом этой реализации является использование ссылочной прозрачности , свойства функционального стиля, где функция всегда возвращает один и тот же результат для одних и тех же входных данных. Поскольку рекурсия является функциональным наследием, мы уверены, что сохраним это свойство для достижения наилучшего результата.
const identity = x =>
x
const concat = (xs, ys) =>
xs .concat (ys)
const genStrings = ([ char, ...rest ], send = identity) =>
// base case: return empty set
char === undefined
? send ([])
// if there is only one char, return singleton result
: rest.length === 0
? send ([char])
// otherwise recur on rest
// 1) add char plus space to each combination
// 2) add char without space to each combination
// 3) concat the result
: genStrings
( rest
, combs =>
send ( concat ( combs .map (c => char + ' ' + c)
, combs .map (c => char + c)
)
)
)
console.log (genStrings ('ABC'))
// [ 'A B C'
// , 'A BC'
// , 'AB C'
// , 'ABC'
// ]
Вы также заметите, что нет необходимости отслеживать несколько других переменных состояния, таких как n
, i
и j
, и не нужно увеличивать их с другими значениями. Программы, использующие меньше выражений и переменных, легче поддерживать и легче отлаживать.
genStrings
работает и с более крупными входами, как пример в вашем вопросе
console.log (genStrings ('ABCDE'))
// [ 'A B C D E'
// , 'A B C DE'
// , 'A B CD E'
// , 'A B CDE'
// , 'A BC D E'
// , 'A BC DE'
// , 'A BCD E'
// , 'A BCDE'
// , 'AB C D E'
// , 'AB C DE'
// , 'AB CD E'
// , 'AB CDE'
// , 'ABC D E'
// , 'ABC DE'
// , 'ABCD E'
// , 'ABCDE'
// ]
Мы также осторожно сделали genStrings
итоговую программу , что означает, что она возвращает действительный результат, даже если входная строка пуста
console.log (genStrings (''))
// []
Поскольку genStrings
определяется с использованием стиля передачи продолжения, мы также можем указать настраиваемое пользователем продолжение на сайте вызова
genStrings ('ABC', console.log)
// [ 'A B C', 'A BC', 'AB C', 'ABC' ]
genStrings ('ABC', combs => combs.length)
// 4
genStrings ('ABC', combs => combs .join (', '))
// 'A B C, A BC, AB C, ABC'
Поскольку genStrings
- это чистая функция с четко определенным доменом (входной) и кодоменом (выходной), нет необходимости в немедленном вызове выражения функции (IIFE), не говоря уже о необходимости отладки.
Я начал изучать его, но не могу понять, что такое синтаксис двоеточия перед rest.length === 0 и genStrings?
?:
- это условный оператор JavaScript , также называемый троичным оператором . Синтаксис conditionExpression ? trueExpression : falseExpression
. Когда conditionExpression
оценивается как истинное значение, оценивается только trueExpression
, и falseExpression
пропускается. И наоборот, если conditionExpression
оценивается как неверное значение, trueExpression
пропускается и оценивается только falseExpression
.
Это экспрессивный эквивалент оператора if
- else
, но вместо того, чтобы полагаться на побочные эффекты, он оценивает значение, как и все другие выражения.
// conditional expression
let someValue =
n === 0 // expression
? "n is zero" // expression
: "n is not zero" // expression
// if statement
let someValue
if (n === 0)
someValue = "n is zero" // side effect
else
someValue = "n is not zero" // side effect
Выше оператор if
является более многословным и основывается на побочных эффектах для установки значения для someValue
. Условное выражение оценивается как значение и может быть непосредственно присвоено переменной.
Аналогично операторам if
- else if
- else
, условные выражения также могут быть объединены в цепочку. Это синтаксис, который вы видите в ответе выше.
const animalSound =
animal === "dog" // if
? "woof" // then
: animal === "cat" // else if
? "meow" // then
: "unknown" // else
Это помогает мне увидеть одну и ту же программу, представленную различными способами. Ниже мы переписываем исходный ответ, используя выражения императивного стиля if
const identity = x =>
x
const concat = (xs, ys) =>
xs .concat (ys)
const genStrings = ([ char, ...rest ], send = identity) =>
{ if (char === undefined)
return send ([])
else if (rest.length === 0)
return send ([char])
else
return genStrings
( rest
, combs =>
send ( concat ( combs .map (c => char + ' ' + c)
, combs .map (c => char + c)
)
)
)
}
console.log (genStrings ('ABC'))
// [ 'A B C'
// , 'A BC'
// , 'AB C'
// , 'ABC'
// ]