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

时间:2017-08-09 08:02:45

标签: javascript ecmascript-6 functional-programming immutability ramda.js

我是函数式编程的学生。我仍然在摆脱旧的变异变异习惯。但有时我会卡住。好的,所以这里有一个问题 - 假设我们有以下闭包

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这样的功能库。我天真的想法在某种程度上使用递归来传递一个新的书籍值,然后传回那个版本。似乎有点过度,所以我想在这个舞台上寻求更多知识渊博的人的帮助。

感谢您的见解!

2 个答案:

答案 0 :(得分:2)

只需在book中保持bookshelf不变即可。这需要每次都创建一个新的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功能 - 您只需使用尚未包含该书的旧值。

如果您希望能够删除刚从任意书架添加的书籍,您还可以同时返回新的书架和卸妆功能:

    …,
    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());

答案 1 :(得分:1)

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

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);

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



{{1}}




这看起来不太好,你可能不想写这样的东西,但它确实能达到原来的效果。