【问题标题】:How do I refactor this composed function with Ramda.js?如何用 Ramda.js 重构这个组合函数?
【发布时间】:2018-04-01 10:21:15
【问题描述】:

我正在使用ramdadata.task 编写一个小型实用程序,它可以从目录中读取图像文件并输出它们的大小。我让它像这样工作:

const getImagePath = assetsPath => item => `${assetsPath}${item}`

function readImages(path) {
  return new Task(function(reject, resolve) {
    fs.readdir(path, (err, images) => {
      if (err) reject(err)
      else resolve(images)
    })
  })
}

const withPath = path => task => {
  return task.map(function(images) {
    return images.map(getImagePath(path))
  })
}

function getSize(task) {
  return task.map(function(images) {
    return images.map(sizeOf)
  })
}

const getImageSize = dirPath => compose(getSize, withPath(dirPath), readImages)

问题在于 withPath 函数将正确的图像路径添加到图像文件名,但强制我的 api 两次传入 directoryName:一次用于读取文件,第二次用于读取路径。这意味着我必须像这样调用getImageSize 函数:

const portfolioPath = `${__dirname}/assets/`

getImageSize(portfolioPath)(portfolioPath).fork(
  function(error) {
    throw error
  },
  function(data) {
    console.log(data)
  }
)

有没有办法将dirname 作为参数只传递一次?我希望 api 像这样工作:

getImageSize(portfolioPath).fork(
  function(error) {
    throw error
  },
  function(data) {
    console.log(data)
  }
)

【问题讨论】:

  • 我现在在我的手机上,所以无法真正测试,但您可以查看chain 如何处理函数:chain(f, g)(x) => f(g(x), x)
  • 嘿@ScottSauyet - 希望引起你的注意。我解决了这个问题,但我仍然想了解在这种情况下我将如何使用链。 chain 不是 Ramda 中的 flatmap 吗?在这里如何应用?
  • 我在想你也许可以将withPath 切换为task => path => ...,然后将getSizechain(withPath, readImages) 组合在一起。至于 chain 如何处理函数,Scott Christopher 提供了一个 excellent answer
  • 好的。我会试试的

标签: javascript node.js functional-programming ramda.js


【解决方案1】:

我设法通过将Task 分辨率传递给一个像这样的对象来解决这个问题:

function readImages(path) {
  return new Task(function(reject, resolve) {
    fs.readdir(path, (err, images) => {
      if (err) reject(err)
      else resolve({ images, path })
    })
  })
}

const withPath = task => {
  return task.map(function({ images, path }) {
    return images.map(getImagePath(path))
  })
}

...然后将其从任务负载中销毁,现在我的 compose 函数如下所示:

module.exports = (function getImageSize(dirPath) {
  return compose(getSize, withPath, readImages)
})()

我的 api 调用如下所示:

getImageSize(portfolioPath).fork(
  function(error) {
    throw error
  },
  function(data) {
    console.log(data)
  }
)

【讨论】:

    【解决方案2】:

    你不应该像那样手动构建路径

    Node 更好的 API 之一是 Path module——我建议您将 readImages 包装器设为通用 readdir 包装器,而是解析 path.resolved 文件路径的数组

    const readdir = dir =>
      new Task ((reject, resolve) =>
        fs.readdir (dir, (err, files) =>
          err
            ? reject (err)
            : resolve (files.map (f => path.resolve (dir, f)))
    
    
    const getImagesSizes = dir =>
      readdir (dir) .map (R.map (sizeOf))
    

    仅仅为了返回 Task 而包装 Node 持续传递样式 API 会很麻烦,不是吗?

    const taskify = f => (...args) =>
      Task ((reject, resolve) =>
        f (...args, (err, x) =>
          err ? reject (err) : resolve (x)))
    
    const readdir = (dir, ...args) =>
      taskify (fs.readdir) (dir, ...args)
        .map (R.map (f => path.resolve (dir, f)))
    
    const getImagesSizes = dir =>
      readdir (dir) .map (R.map (sizeOf))
    

    您可能还应该注意归档 目录 的文件路径 - 除非您的 sizeOf 实现处理该路径

    【讨论】:

    • 我认为对于大多数初学函数式程序员来说,将所有问题都视为composition 问题是很常见的——别担心;这会随着时间而改变
    • 但是你展示的也是一种构图模式不是吗?您能否进一步扩展您的批评?我是 FP 的相对初学者,主要将 FP 作为一系列函数组合来学习。
    • 阿米特,谢谢你强调了一个歧义——我打算区分 "[function] composition"compose 函数本身——它您可以将所有复合函数视为较小函数的组合,但请记住并非所有函数组合都是用 compose 函数编写的 - 所以是的,readdir 是一个 composition 但在这种情况下,compose 函数并不能帮助我们更好地表达该组合 - 大多数初学者会说“我需要一个组合”,到达 compose 函数,然后想知道为什么他们的最终程序感觉很尴尬。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-28
    • 1970-01-01
    • 2018-03-27
    • 1970-01-01
    • 2011-01-25
    • 2022-01-01
    • 2015-11-13
    相关资源
    最近更新 更多