我有一个CSV文件,有两个列,文本和计数。其目标是从以下文件中转换该文件:
some text once,1
some text twice,2
some text thrice,3对此:
some text once,1
some text twice,1
some text twice,1
some text thrice,1
some text thrice,1
some text thrice,1重复每一行计数次数,并将计数扩展到这几行。
在我看来,这似乎是Seq.unfold的一个很好的候选,在我们读取文件时生成额外的行。我有以下生成器功能:
let expandRows (text:string, number:int32) =
if number = 0
then None
else
let element = text // "element" will be in the generated sequence
let nextState = (element, number-1) // threaded state replacing looping
Some (element, nextState)FSI产生以下函数签名:
val expandRows : text:string * number:int32 -> (string * (string * int32)) option在FSI中执行以下操作:
let expandedRows = Seq.unfold expandRows ("some text thrice", 3)产生预期的:
val it : seq<string> = seq ["some text thrice"; "some text thrice"; "some text thrice"]问题是:如何将其插入到更大的ETL管道的上下文中?例如:
File.ReadLines(inFile)
|> Seq.map createTupleWithCount
|> Seq.unfold expandRows // type mismatch here
|> Seq.iter outFile.WriteLine下面的错误发生在管道上下文中的expandRows上。
Type mismatch.
Expecting a 'seq<string * int32> -> ('a * seq<string * int32>) option'
but given a 'string * int32 -> (string * (string * int32)) option'
The type 'seq<string * int 32>' does not match the type 'string * int32'我原以为expandRows会返回字符串的seq,就像在我的孤立测试中一样。因为这既不是“期待”,也不是“给予”,我很困惑。有人能给我指明正确的方向吗?
代码的要点如下:https://gist.github.com/akucheck/e0ff316e516063e6db224ab116501498
发布于 2016-12-29 06:54:48
Seq.map生成一个序列,但是Seq.unfold不接受一个序列,它只需要一个值。因此,您不能直接将Seq.map的输出输送到Seq.unfold。您需要逐个元素来代替它。
但是,对于每个元素,您的Seq.unfold将生成一个序列,因此最终的结果将是序列序列。您可以使用Seq.collect在一个序列中收集所有这些“子序列”。
File.ReadLines(inFile)
|> Seq.map createTupleWithCount
|> Seq.collect (Seq.unfold expandRows)
|> Seq.iter outFile.WriteLineSeq.collect接受一个函数和一个输入序列。对于输入序列的每个元素,函数应该生成另一个序列,而Seq.collect将将所有这些序列连接在一个序列中。您可能会认为Seq.collect是Seq.map和Seq.concat在一个函数中的组合。另外,如果您来自C#,那么在那里Seq.collect被称为SelectMany。
发布于 2016-12-29 07:01:03
在这种情况下,由于您只是想多次重复某个值,所以没有理由使用Seq.unfold。您可以使用Seq.replicate代替:
// 'a * int -> seq<'a>
let expandRows (text, number) = Seq.replicate number text您可以使用Seq.collect编写它:
File.ReadLines(inFile)
|> Seq.map createTupleWithCount
|> Seq.collect expandRows
|> Seq.iter outFile.WriteLine事实上,这个版本的expandRows所执行的唯一工作是“解压缩”一个元组,并将其值组合成咖喱形式。
虽然F#在其核心库中没有提供这样一个通用函数,但是您可以很容易地定义它(和其他类似有用的职能):
module Tuple2 =
let curry f x y = f (x, y)
let uncurry f (x, y) = f x y
let swap (x, y) = (y, x)这将使您能够从著名的功能构建块组成管道:
File.ReadLines(inFile)
|> Seq.map createTupleWithCount
|> Seq.collect (Tuple2.swap >> Tuple2.uncurry Seq.replicate)
|> Seq.iter outFile.WriteLine发布于 2016-12-29 06:50:05
听起来你想做的是
File.ReadLines(inFile)
|> Seq.map createTupleWithCount
|> Seq.map (Seq.unfold expandRows) // Map each tuple to a seq<string>
|> Seq.concat // Flatten the seq<seq<string>> to seq<string>
|> Seq.iter outFile.WriteLine看起来,您希望通过Seq.unfold和expandRows将序列中有计数的每个元组转换为一个Seq.unfold。这是通过映射完成的。
之后,您希望将您的seq<seq<string>>压平成一个大的seq<string>,这是通过Seq.concat实现的。
https://stackoverflow.com/questions/41374343
复制相似问题