функциональный 101
Идиоматическое решение c JavaScript: map
- join
. Но функциональное программирование - это разбиение программы на повторно используемые модули и создание барьеров для абстракции -
// Task.js
const empty =
{ id: 0, task: "" }
const task = (id = 0, task = "") =>
({ id, task })
const toString = (t = empty) =>
`${t.id}. ${t.task}`
const toStringAll = ([ first, ...rest ]) =>
rest.reduce // <-- reduce
( (r, x) => r + ", " + toString(x)
, toString(first)
)
export { empty, task, toString, toStringAll }
Итак, есть возможная и разумная реализация с использованием reduce
. Удобочитаемость этой программы хороша, потому что каждая часть модуля небольшая и выполняет только одну вещь.
Теперь мы закрываем воображаемую крышку на нашем модуле и забываем всю сложность внутри. Остается чистый интерфейс, который четко передает возможности модуля -
// Main.js
import { task, toStringAll } from './Task'
const data =
[ task(7, "foo")
, task(22, "bar")
, task(33, "qux")
]
console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux
console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar
console.log(toStringAll(data.slice(0,1)))
// 7. foo
console.log(toStringAll(data.slice(0,0)))
// 0.
Разверните приведенный ниже фрагмент, чтобы проверить результат в своем браузере -
const empty =
{ id: 0, task: "" }
const task = (id = 0, task = "") =>
({ id, task })
const toString = (t = empty) =>
`${t.id}. ${t.task}`
const toStringAll = ([ first, ...rest ]) =>
rest.reduce
( (r, x) => r + ", " + toString(x)
, toString(first)
)
const data =
[ task(7, "foo")
, task(22, "bar")
, task(33, "qux")
]
console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux
console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar
console.log(toStringAll(data.slice(0,1)))
// 7. foo
console.log(toStringAll(data.slice(0,0)))
// 0.
барьер абстракции
Main.js
отделен от Task
барьером абстракции. Теперь мы можем выбрать любую реализацию для toString
и toStringAll
, не требуя от Main
заботы о том, как Task
работает под крышкой.
Попрактикуемся в внесении изменений и обновим, как empty
задача представлена. В приведенной выше программе мы видим 0.
, но вместо этого мы заставим его сказать Empty.
. И просто для удовольствия, давайте попробуем новую реализацию toStringAll
-
// Task.js
const empty = //
const task = //
const toString = (t = empty) =>
t === empty
? `Empty.` // <-- custom representation for empty
: `${t.id}. ${t.task}`
const toStringAll = ([ t = empty, ...more ]) =>
more.length === 0
? toString(t)
: toString(t) + ", " + toStringAll(more) // <-- recursive
//
export { empty, task, toString, toStringAll }
Main
, не нужно делать ничего по-другому -
// Main.js
import { task, toStringAll } from './Task'
const data = //
console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux
console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar
console.log(toStringAll(data.slice(0,1)))
// 7. foo
console.log(toStringAll(data.slice(0,0)))
// Empty.
Разверните фрагмент ниже, чтобы проверьте результат в своем браузере -
const empty =
{ id: 0, task: "" }
const task = (id = 0, task = "") =>
({ id, task })
const toString = (t = empty) =>
t === empty
? `Empty.`
: `${t.id}. ${t.task}`
const toStringAll = ([ t = empty, ...more ]) =>
more.length === 0
? toString(t)
: toString(t) + ", " + toStringAll(more)
const data =
[ task(7, "foo")
, task(22, "bar")
, task(33, "qux")
]
console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux
console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar
console.log(toStringAll(data.slice(0,1)))
// 7. foo
console.log(toStringAll(data.slice(0,0)))
// Empty.