尝试重构此代码以仅使用不可变结构

Trying to refactor this code to use only immutable structures

我是函数式编程的学生。我仍在戒除旧的可变突变习惯。但有时我会卡住。好的,问题来了——假设我们有以下闭包

const bookShelf = () => {
  let books = []
  const listBooks = () => books
  const addBook = (book) => {
    books = books.concat(book)
    return function removeBook() { books = books.filter( b => b !== book ) }
  }
  return {addBook,listBooks}
}

const { addBook, listBooks } = bookShelf()
const removeMobyDick = addBook('Moby Dick')
const removeWalden = addBook('Walden')
removeWalden()
console.log(listBooks()) // ["Moby Dick"]

请注意,我有一个对象发生了变化:书籍。

我的问题是,我如何重构这段代码,使 books 不可变,但我获得相同的最终结果。如果需要,请随意使用像 Ramda 这样的功能库。我天真的想法是以某种方式使用递归来传递书籍的新价值,然后将该版本传回。似乎有点过分,所以我想向在这个领域更有知识的人寻求帮助。

感谢您的见解!

书架类型在这里似乎并没有真正完成任何事情,所以让我们把它做成一个列表(数组)。

let bookshelf = [];

现在您似乎想要一种方法来生成包含新项目的列表以及一种从列表中删除该项目的方法。有点奇怪,但你可以通过在元组(数组)中返回两者来做到这一点:

const remove = (list, value) =>
    list.filter(x => x !== value);

const addRemovable = (list, value) =>
    [[...list, value], list => remove(list, value)];


let bookshelf = [];
let removeMobyDick;
let removeWalden;

[bookshelf, removeMobyDick] = addRemovable(bookshelf, 'Moby Dick');
[bookshelf, removeWalden] = addRemovable(bookshelf, 'Walden');

bookshelf = removeWalden(bookshelf);

console.log(bookshelf);

这看起来不太好,你可能不想写类似的东西,但它确实实现了与你原来的一样的东西。

只需在 bookshelf 中保留 book 常量。当然,这需要每次都创建一个新的 bookshelf,所以最简单的方法是使 books 成为函数的参数:

function bookShelf(books) {
  return {
    listBooks() { return books },
    addBook(book) { return bookShelf(books.concat([book])); }
  }
}

const empty = bookShelf([]);
const shelfWithMobyDick = empty.addBook('Moby Dick');
const shelfWithMobyDickAndWalden = shelfWithMobyDick.addBook('Walden');
console.log(shelfWithMobyDick.listBooks());

如您所见,不需要 removeBook 函数 - 您只需使用尚未包含该书的旧值。

如果您希望能够从任意书架中移除刚刚添加的书,您还可以return新书架和移除功能:

    …,
    addBook(book) {
      return {
        bookShelf: bookShelf(books.concat([book]));
        removeThis(shelf) { return bookShelf(shelf.listBooks().filter(b => b !== book)); }
      };
    }

用作

const empty = bookShelf([]);
const {bookShelf: shelfWithMobyDick, removeThis: removeMobyDick} = empty.addBook('Moby Dick');
const {bookShelf: shelfWithMobyDickAndWalden, removeThis: removeWalden} = shelfWithMobyDick.addBook('Walden');
const shelfWithWalden = removeMobyDick(shelfWithMobyDickAndWalden);
console.log(shelfWithWalden.listBooks());