var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
В результате объекты формируют цепочку:
Списки удобны тем, что они могут делиться частью своей структуры. Например, можно сделать два списка, {value: 0, rest: list}
и {value: -1, rest: list}
, где list
– это ссылка на ранее объявленную переменную. Это два независимых списка, при этом у них есть общая структура list
, которая включает три последних элемента каждого из них. Кроме того, оригинальный список также сохраняет свои свойства как отдельный список из трёх элементов.
Напишите функцию arrayToList
, которая строит такую структуру, получая в качестве аргумента [1, 2, 3]
, а также функцию listToArray
, которая создаёт массив из списка. Также напишите вспомогательную функцию prepend
, которая получает элемент и создаёт новый список, где этот элемент добавлен спереди к первоначальному списку, и функцию nth
, которая в качестве аргументов принимает список и число, а возвращает элемент на заданной позиции в списке или же undefined
в случае отсутствия такого элемента.
Если ваша версия nth
нерекурсивна, тогда напишите её рекурсивную версию.
console.log(arrayToList([10, 20]));
// → {value: 10, rest: {value: 20, rest: null}}
console.log(listToArray(arrayToList([10, 20, 30])));
// → [10, 20, 30]
console.log(prepend(10, prepend(20, null)));
// → {value: 10, rest: {value: 20, rest: null}}
console.log(nth(arrayToList([10, 20, 30]), 1));
// → 20
Глубокое сравнение
Оператор ==
сравнивает переменные объектов, проверяя, ссылаются ли они на один объект. Но иногда полезно было бы сравнить объекты по содержимому.
Напишите функцию deepEqual
, которая принимает два значения и возвращает true
, только если это два одинаковых значения или это объекты, свойства которых имеют одинаковые значения, если их сравнивать рекурсивным вызовом deepEqual
.
Чтобы узнать, когда сравнивать величины через ===
, а когда – объекты по содержимому, используйте оператор typeof
. Если он выдаёт "object"
для обеих величин, значит нужно делать глубокое сравнение. Примите во внимание одно дурацкое исключение, существующее по историческим причинам: typeof null
тоже возвращает "object"
.
var obj = {here: {is: "an"}, object: 2};
console.log(deepEqual(obj, obj));
// → true
console.log(deepEqual(obj, {here: 1, object: 2}));
// → false
console.log(deepEqual(obj, {here: {is: "an"}, object: 2}));
// → true
5. Функции высшего порядка
Цу-ли и Цу-су похвалялись размерами своих новых программ. «Двести тысяч строк»,- сказал Цу-ли,- «не считая комментариев!» Цу-су ответил: «Пф-ф, моя – почти миллион строк». Мастер Юнь-Ма сказал: «Моя лучшая программа занимает пятьсот строк». Услышав это, Цу-ли и Цу-су испытали просветление.
Есть два способа построения программ: сделать их настолько простыми, что там очевидно не будет ошибок, или же настолько сложными, что там не будет очевидных ошибок.
Большая программа – затратная программа, и не только из-за времени её написания. Большой размер обычно означает сложность, а сложность сбивает с толку программистов. Сбитые с толку программисты делают ошибки в программах. Большая программа означает, что багам есть где спрятаться, и их получается труднее отыскать.
Вернёмся ненадолго к двум примерам из введения. Первый самодостаточен и занимает шесть строк.
var total = 0, count = 1;
while (count <= 10) {
total += count;
count += 1;
}
console.log(total);
Второй основан на двух внешних функциях и занимает одну строку.
console.log(sum(range(1, 10)));
В каком из них скорее встретится ошибка?
Если мы добавим размер определений sum
и range
, вторая программа тоже получится большой – больше первой. Но я всё равно утверждаю, что она скорее всего будет правильной.
Это будет потому, что выражение решения непосредственно относится к решаемой задаче. Суммирование числового промежутка – это не циклы и счётчики. Это суммы и промежутки.