在 Scala 中,用 Try 操作处理一个项目列表,并保留原始项目以报告可能的失败
In Scala, process a list of items with a Try operation, and keep the original item to report the possible failure
我有一个字符串列表,我想对每个项目进行多次转换。我想保留原始字符串,以便在它无效时显示它。示例如下:
import scala.util.Try
val list = List("<entry id='1'/>", "haha", "<entry id='hehe'/>")
def parseXML(str: String) = Try { xml.XML.loadString(str) }
list
.map(parseXML)
.map(tryEntry => tryEntry.map(entry => (entry \ "@id").text))
.map(tryId => tryId.flatMap(id => Try(id.toInt)))
// here I lose the original string
res17: List[Try[Int]] = List(
Success(1),
Failure(org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.),
Failure(java.lang.NumberFormatException: For input string: "hehe")
)
// here I keep a copy of the original string, so I can report the invalid entry to the user
list
.map(l => (l, parseXML(l)))
.map { case(line, tryEntry) => (line, tryEntry.map(entry => (entry \ "@id").text)) }
.map { case(line, tryId) => (line, tryId.flatMap(id => Try(id.toInt))) }
res19: List[(String, Try[Int])] = List(
("<entry id='1'/>", Success(1)),
("haha", Failure(org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.)),
("<entry id='hehe'/>", Failure(java.lang.NumberFormatException: For input string: "hehe"))
)
在res19中,我保留了一份原字符串,这样可以报错和原字符串。但是,我每次在映射操作中都需要携带这些信息,这很丑陋。有没有更好的办法? (也许使用 ScalaZ State
和 for
?)
这可以通过添加一个额外的特征 ParseContext[T]
优雅地解决,除了原始上下文,例如行号和 'original' 文本,它是一个函子(.map(f: T => T)
) 和一个 Monad (.flatMap(f: T => ParseContext[T])
)。您可以选择创建两个 ParseContext
的实例,命名为 Success
和 Failure
,以指示解析期间可能失败。
基本上,您可以使用一些额外的上下文信息来扩展 Try
特征。
或许,将多个 map
合并为一个会有帮助?
val result: List[(String, Try[Int])] = list
.map { line =>
line -> parseXML(line)
.map(_ \ "@id")
.text
.flatMap(id => Try(id.toInt)
}
如果列表中没有重复的字符串,你可以把它做成一个映射,然后mapValues
覆盖它:
(l zip l toMap)
.mapValues(parseXML)
.mapValues(tryEntry => tryEntry.map(entry => (entry \ "@id").text))
.mapValues(tryId => tryId.flatMap(id => Try(id.toInt)))
.toList
我有一个字符串列表,我想对每个项目进行多次转换。我想保留原始字符串,以便在它无效时显示它。示例如下:
import scala.util.Try
val list = List("<entry id='1'/>", "haha", "<entry id='hehe'/>")
def parseXML(str: String) = Try { xml.XML.loadString(str) }
list
.map(parseXML)
.map(tryEntry => tryEntry.map(entry => (entry \ "@id").text))
.map(tryId => tryId.flatMap(id => Try(id.toInt)))
// here I lose the original string
res17: List[Try[Int]] = List(
Success(1),
Failure(org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.),
Failure(java.lang.NumberFormatException: For input string: "hehe")
)
// here I keep a copy of the original string, so I can report the invalid entry to the user
list
.map(l => (l, parseXML(l)))
.map { case(line, tryEntry) => (line, tryEntry.map(entry => (entry \ "@id").text)) }
.map { case(line, tryId) => (line, tryId.flatMap(id => Try(id.toInt))) }
res19: List[(String, Try[Int])] = List(
("<entry id='1'/>", Success(1)),
("haha", Failure(org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.)),
("<entry id='hehe'/>", Failure(java.lang.NumberFormatException: For input string: "hehe"))
)
在res19中,我保留了一份原字符串,这样可以报错和原字符串。但是,我每次在映射操作中都需要携带这些信息,这很丑陋。有没有更好的办法? (也许使用 ScalaZ State
和 for
?)
这可以通过添加一个额外的特征 ParseContext[T]
优雅地解决,除了原始上下文,例如行号和 'original' 文本,它是一个函子(.map(f: T => T)
) 和一个 Monad (.flatMap(f: T => ParseContext[T])
)。您可以选择创建两个 ParseContext
的实例,命名为 Success
和 Failure
,以指示解析期间可能失败。
基本上,您可以使用一些额外的上下文信息来扩展 Try
特征。
或许,将多个 map
合并为一个会有帮助?
val result: List[(String, Try[Int])] = list
.map { line =>
line -> parseXML(line)
.map(_ \ "@id")
.text
.flatMap(id => Try(id.toInt)
}
如果列表中没有重复的字符串,你可以把它做成一个映射,然后mapValues
覆盖它:
(l zip l toMap)
.mapValues(parseXML)
.mapValues(tryEntry => tryEntry.map(entry => (entry \ "@id").text))
.mapValues(tryId => tryId.flatMap(id => Try(id.toInt)))
.toList