首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在F#中,如何在更大的管道上下文中使用Seq.unfold?

在F#中,如何在更大的管道上下文中使用Seq.unfold?
EN

Stack Overflow用户
提问于 2016-12-29 06:33:44
回答 3查看 479关注 0票数 7

我有一个CSV文件,有两个列,文本和计数。其目标是从以下文件中转换该文件:

代码语言:javascript
复制
some text once,1
some text twice,2
some text thrice,3

对此:

代码语言:javascript
复制
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的一个很好的候选,在我们读取文件时生成额外的行。我有以下生成器功能:

代码语言:javascript
复制
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产生以下函数签名:

代码语言:javascript
复制
val expandRows : text:string * number:int32 -> (string * (string * int32)) option

在FSI中执行以下操作:

代码语言:javascript
复制
let expandedRows = Seq.unfold expandRows ("some text thrice", 3)

产生预期的:

代码语言:javascript
复制
val it : seq<string> = seq ["some text thrice"; "some text thrice"; "some text thrice"]

问题是:如何将其插入到更大的ETL管道的上下文中?例如:

代码语言:javascript
复制
File.ReadLines(inFile)                  
    |> Seq.map createTupleWithCount
    |> Seq.unfold expandRows // type mismatch here
    |> Seq.iter outFile.WriteLine

下面的错误发生在管道上下文中的expandRows上。

代码语言:javascript
复制
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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-12-29 06:54:48

Seq.map生成一个序列,但是Seq.unfold不接受一个序列,它只需要一个值。因此,您不能直接将Seq.map的输出输送到Seq.unfold。您需要逐个元素来代替它。

但是,对于每个元素,您的Seq.unfold将生成一个序列,因此最终的结果将是序列序列。您可以使用Seq.collect在一个序列中收集所有这些“子序列”。

代码语言:javascript
复制
File.ReadLines(inFile) 
    |> Seq.map createTupleWithCount 
    |> Seq.collect (Seq.unfold expandRows)
    |> Seq.iter outFile.WriteLine

Seq.collect接受一个函数和一个输入序列。对于输入序列的每个元素,函数应该生成另一个序列,而Seq.collect将将所有这些序列连接在一个序列中。您可能会认为Seq.collectSeq.mapSeq.concat在一个函数中的组合。另外,如果您来自C#,那么在那里Seq.collect被称为SelectMany

票数 6
EN

Stack Overflow用户

发布于 2016-12-29 07:01:03

在这种情况下,由于您只是想多次重复某个值,所以没有理由使用Seq.unfold。您可以使用Seq.replicate代替:

代码语言:javascript
复制
// 'a * int -> seq<'a>
let expandRows (text, number) = Seq.replicate number text

您可以使用Seq.collect编写它:

代码语言:javascript
复制
File.ReadLines(inFile)
|> Seq.map createTupleWithCount
|> Seq.collect expandRows
|> Seq.iter outFile.WriteLine

事实上,这个版本的expandRows所执行的唯一工作是“解压缩”一个元组,并将其值组合成咖喱形式。

虽然F#在其核心库中没有提供这样一个通用函数,但是您可以很容易地定义它(和其他类似有用的职能):

代码语言:javascript
复制
module Tuple2 =
    let curry f x y = f (x, y)    
    let uncurry f (x, y) = f x y    
    let swap (x, y) = (y, x)

这将使您能够从著名的功能构建块组成管道:

代码语言:javascript
复制
File.ReadLines(inFile)
|> Seq.map createTupleWithCount
|> Seq.collect (Tuple2.swap >> Tuple2.uncurry Seq.replicate)
|> Seq.iter outFile.WriteLine
票数 6
EN

Stack Overflow用户

发布于 2016-12-29 06:50:05

听起来你想做的是

代码语言:javascript
复制
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.unfoldexpandRows将序列中有计数的每个元组转换为一个Seq.unfold。这是通过映射完成的。

之后,您希望将您的seq<seq<string>>压平成一个大的seq<string>,这是通过Seq.concat实现的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41374343

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档