如何存储功能链的数据?

时间:2018-07-11 06:57:43

标签: javascript list object functional-programming chain

下面的一个简单功能:

const L = a => L;

表格

L
L(1)
L(1)(2)
...

这似乎形成一个列表,但是实际数据根本没有存储,因此,如果需要存储诸如[1,2]之类的数据,完成任务的最明智的做法是什么?

const L = (a) => {
 // do somthing
  return L;
};

我更喜欢这种简洁的箭头功能样式,并且不想破坏尽可能多的外部结构。当然,我知道需要对外部结构进行一些修改,但是我很好奇这是可能的,尤其是在功能风格而非OO中。

该规范只是存储功能链的数据。

有什么想法吗?谢谢。

最简单的初始方法是:

const L = (a) => {
  L.val = a;
  return L;
};
L.val = L;

可以执行一些操作,但没有数据累积。

{ [Function: L] val: [Circular] }
{ [Function: L] val: 1 }
{ [Function: L] val: 2 }

注意:

每个列表都应独立于累积。

L(3)(4)

将返回[3,4]而不是[2,3,3,4],并且会先累积其他列表。

高级主题!

How to store data of a functional chain of Monoidal List?

4 个答案:

答案 0 :(得分:5)

函数currying和可变参数实际上并不能一起工作。一旦意识到以下两个表达式不兼容,这一限制就会变得显而易见

L (1)     -> [ 1 ]
L (1) (2) -> [ 1, 2 ]

L (1)之上返回一个列表,但是在第二个表达式中,我们希望L (1)是可以应用于2的函数。 L (1)可以是列表,它可以是产生列表的函数;不能同时使用。

这就是为什么其他人提出诸如.list之类的东西来获取实际价值的原因。您可以这样做,但是知道不需要使用对象属性或依靠突变。您可以使用任何选择的信号

const L = (x, acc = []) =>
  x === undefined
    ? acc
    : y => L (y, [...acc, x])
    
console.log
  ( L ()              // []
  , L (1) ()          // [ 1 ]
  , L (1) (2) ()      // [ 1, 2 ]
  , L (1) (2) (3) ()  // [ 1, 2, 3 ]
  )

我们可以通过使用辅助帮助器函数来抽象掉可选参数。这种技术与您发现的解决方案类似,但是在这里我们避免了将值笨拙地分配给函数属性,而是使用简单的变量和非突变动作

const L = init =>
{ const loop = (acc, x) =>
    x === undefined
      ? acc
      : y => loop ([...acc, x], y)
  return loop ([], init)
}

console.log
  ( L ()              // []
  , L (1) ()          // [ 1 ]
  , L (1) (2) ()      // [ 1, 2 ]
  , L (1) (2) (3) ()  // [ 1, 2, 3 ]
  )

或者您的需求看起来有些灵活,可以使用更灵活的编码来发挥创意

const List = x =>
  k => k (x)
  
const append = x => xs =>
  List ([ ...xs, x ])

const prepend = x => xs =>
  List ([ x, ...xs ])
  
List ([]) (append (1)) (console.log)
// [ 1 ]

List ([ 2, 3 ]) (append (4)) (append (5)) (prepend (1)) (console.log)
// [ 1, 2, 3, 4, 5 ]

将JavaScript的允许语法推到极限是很有趣的,但是可变参数函数最好使用传播参数来定义

const L = (...values) =>
  values

console.log
  ( L ()         // []
  , L (1)        // [ 1 ]
  , L (1, 2)     // [ 1, 2 ]
  , L (1, 2, 3)  // [ 1, 2, 3 ]
  )

一个不太人为的例子展示了一个更好的用例

const max = (x, ...ys) =>
  ys.length === 0
    ? x
    : max2 (x, max (...ys))
    
const max2 = (x, y) =>
   x > y ? x : y
   
console.log
  ( max (1, 5, 3)     // 5
  , max (5, 2, 9, 7)  // 9
  , max (4)           // 4
  , max ()            // undefined
  )

答案 1 :(得分:2)

以下方法可能有点疯狂,但它可以起作用:

const L = x => {
    const L_ = xs => x => typeof x == 'undefined' ? xs : L_ ([...xs, x])
    
    return L_ ([]) (x)
}

const l1 = L (0) (2) (55) (383) (91) (6) ()
const l2 = L (22) (985) (82) (12) (1034) ()

console.log(l1)
console.log(l2)

基本上,外部L接收第一个要添加到列表中的元素。随后的调用将递归内部L_并串联先前的xs和新的x,直到x评估 falsy * undefined

请注意内部L_是如何部分应用的,因此下一次调用只需要一个新的x

另一种方法: Writer monad

还有另一种使用 Writer monad 的方法。请注意,为示例起见,简化了此示例实现:

/////
const Writer = acc => {
    return { acc }
}

const of = x => Writer ([x])
const map = f => m => Writer ([...m.acc, f (m.acc)])
const read = m => m.acc
/////

const pipe = xs => x => xs.reduce ((o, f) => f (o), x)
const K = x => () => x


const list = pipe ([ 
   of,
   map (K (1)), 
   map (K (38)), 
   map (K (1781)), 
   read
]) (123)

const list2 = pipe ([ 
   of,
   map (x => x * 2), 
   map (x => x + 4), 
   map (x => x * 10), 
   read
]) (123)

console.log ('#1 list:')
console.log (list)
console.log ('#2 list:')
console.log (list2)

* 它不应该虚假,因为0等于false,因此L不支持该值

答案 2 :(得分:1)

由于@Adelin的友好和熟练,我提出了理想的方法。

  const L = (a) => {
      const m = a => (m.list ? m.list : m.list = [])
          .push(a) && m;  //use `concat` and refactor needed instead of `push` that is not immutable
      return m(a); // Object construction!
    };

console.log(L);
console.log(L(2));
console.log(L(1)(2)(3))

一些输出:

{ [Function: m] list: [ 2 ] } 
{ [Function: m] list: [ 1, 2, 3 ] }

这是多么优雅。

95%的贡献再次归功于@Adelin。 非常感谢。

答案 3 :(得分:0)

这是我能达到的最接近的水平。

const L = a => b => {
  if (!Array.isArray(a)) a = [a];
  a.push(b);
  return a;
}

x = L(L(L(3)(5))(5))(3)

console.log(x); // [3, 5, 5, 3]

我希望你喜欢括号!