关于这个话题已经有很多讨论,但我都是关于鞭打死马,特别是当我发现他们可能还在呼吸时.
我正在研究解析CSV的异常和异国情调的文件格式,为了好玩,我决定用我知道的2 .net语言C#和F#来表征性能.
结果......令人不安.F#大幅提升了2倍或更多(实际上我认为它更像是.5n,但是由于我正在测试硬件IO,因此获得真正的基准测试很难).
像读取CSV这样常见的性能特征让我感到惊讶(请注意,系数意味着C#在非常小的文件上获胜.我正在进行的测试越多,感觉C#的表现越差,这既令人惊讶又令人关注,因为它可能意味着我做错了).
一些笔记:Core 2 duo笔记本电脑,主轴磁盘80演出,3演出ddr 800内存,Windows 7 64位溢价,.Net 4,没有打开电源选项.
第一次运行后30,000行5宽1短语10个字符或更少给我3个支持尾调用递归(它似乎缓存文件)
对于尾部调用递归,300,000(重复相同的数据)是2的因子,F#的可变实现略微胜出,但性能签名表明我正在击中磁盘而不是ram-disking整个文件,这会导致半随机性能尖峰.
F#代码
//Module used to import data from an arbitrary CSV source
module CSVImport
open System.IO
//imports the data froma path into a list of strings and an associated value
let ImportData (path:string) : List<string []> =
//recursively rips through the file grabbing a line and adding it to the
let rec readline (reader:StreamReader) (lines:List<string []>) : List<string []> =
let line = reader.ReadLine()
match line with …Run Code Online (Sandbox Code Playgroud) 在查看Real World Haskell中的CSV示例代码之后,我尝试构建一个小的XML解析器.但是关闭标签会因"意外"/"错误而错误输出.你能告诉我为什么我的"closeTag"解析器不起作用(或者可能不会被调用)吗?谢谢!
import Text.ParserCombinators.Parsec
xmlFile = manyTill line eof
line = manyTill tag eol
eol = char '\n'
word = many1 (noneOf "></")
tag = choice [openTag, closeTag, nullTag, word]
nullTag = between (char '<') (string "/>") word
closeTag = between (string "</") (char '>') word
openTag = between (char '<') (char '>') tagContent
attrval = between (char '"') (char '"') word
atts = do {
(char ' ')
; sepBy attr (char ' ')
}
attr = do …Run Code Online (Sandbox Code Playgroud) 我现在正在试验Haskell并且非常享受这种体验,但我正在评估它是否具有一些相当严格的性能要求的真实项目.我的任务的第一步是处理维基百科的完整(无历史)转储(bzip) - 总共约6Gb压缩.在python中,每个原始页面(总共大约1000万)的完整提取的脚本在我的盒子上大约需要30分钟(对于参考,使用pull解析器的scala实现大约需要40分钟).我一直试图使用Haskell和ghc复制这个性能,并且一直在努力匹配这个.
我一直在使用Codec.Compression.BZip进行解压缩,使用hexpat进行解析.我使用lazy bytestrings作为hexpat的输入,使用严格的字节串作为元素文本类型.为了提取每个页面的文本,我正在构建一个指向文本元素的指针的Dlist,然后迭代它以将其转储到stdout.我刚刚描述的代码已经通过了许多分析/重构迭代(我很快从字符串转换到字节串,然后从字符串连接转移到指向文本的指针列表 - 然后转到指向文本的指针列表).我认为我从原始代码中获得了大约2个数量级的加速,但它仍需要一个半小时来解析(尽管它有一个可爱的小内存占用).所以我正在寻找社区的一些灵感,让我更加努力.代码如下(为了从分析器中获取更多细节,我将其分解为许多子功能).请原谅我的Haskell - 我只编写了几天(与Real World Haskell共度了一个星期).并提前感谢!
import System.Exit
import Data.Maybe
import Data.List
import Data.DList (DList)
import qualified Data.DList as DList
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy as LazyByteString
import qualified Codec.Compression.BZip as BZip
import Text.XML.Expat.Proc
import Text.XML.Expat.Tree
import Text.XML.Expat.Format
testFile = "../data/enwiki-latest-pages-articles.xml.bz2"
validPage pageData = case pageData of
(Just _, Just _) -> True
(_, _) -> False
scanChildren :: [UNode ByteString] -> DList ByteString
scanChildren c = case c …Run Code Online (Sandbox Code Playgroud) 以下类型扩展名
module Dict =
open System.Collections.Generic
type Dictionary<'K, 'V> with
member this.Difference(that:Dictionary<'K, 'T>) =
let dict = Dictionary()
for KeyValue(k, v) in this do
if not (that.ContainsKey(k)) then
dict.Add(k, v)
dict
Run Code Online (Sandbox Code Playgroud)
给出错误:
签名和实现不兼容,因为类型参数'TKey'的声明需要形式为'TKey:equality的约束
但是当我添加约束时,它会给出错误:
此类型扩展的声明类型参数与原始类型"Dictionary < , >" 上的声明类型参数不匹配
这尤其神秘,因为以下类型扩展没有约束并且有效.
type Dictionary<'K, 'V> with
member this.TryGet(key) =
match this.TryGetValue(key) with
| true, v -> Some v
| _ -> None
Run Code Online (Sandbox Code Playgroud)
现在我有一些奇怪的想法:只有在访问某些成员时才需要约束吗?
什么是Prolog运算符^?
查看Prolog内置指令op,列出了内置运算符.
我看
**是取幂
/ \是或
但是^是什么?
当前三个答案中的每一个都是有价值的,我学到了一些东西:
我有一个NUnit单元测试,它是用普通的F#库编写的,但它是针对可移植类库中的F#代码.
当我运行此测试(在Visual Studio 2013中)时,我得到以下异常:
Result Message: System.MissingMethodException : Method not found:
'Microsoft.FSharp.Control.FSharpAsync`1<System.IO.TextReader> FSharp.Data.Runtime.IO.asyncReadTextAtRuntime(System.Boolean, System.String, System.String, System.String, System.String)'.
Run Code Online (Sandbox Code Playgroud)
这就是我在Portable Class Library中的app.config中所拥有的:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.3.1.0" newVersion="3.3.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Run Code Online (Sandbox Code Playgroud)
这就是我在普通F#库的app.config中所拥有的:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.3.1.0" newVersion="4.3.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="nunit.framework" publicKeyToken="96d09a1eb7f44a77" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.3.13283" newVersion="2.6.3.13283" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Run Code Online (Sandbox Code Playgroud) 考虑我有两种不同的库类型:
type Foo = { foo : string }
type Bar = { bar : int32 }
Run Code Online (Sandbox Code Playgroud)
我想实现zoo适用于任一个Foo或Bar实例的泛型函数.我无法改变Foo,Bar因为它们是图书馆代码的一部分.
下面是使用类型扩展和内联函数的解释了我的第一次尝试在这里:
// Library.fs
module Library
type Foo = { foo : string }
type Bar = { bar : int32 }
// Program.fs
type Foo with
static member zoo (f : Foo) = "foo"
type Bar with
static member zoo (b : Bar) = "bar"
let inline …Run Code Online (Sandbox Code Playgroud) 在F#编译器的同一范围内使用相同的变量两次时,没有警告或反馈.例如
let s = "abc"
let s = "def"
printfn "%A" s
Run Code Online (Sandbox Code Playgroud)
结果是
def
Run Code Online (Sandbox Code Playgroud)
我已经看到
有没有办法在Visual Studio中对F#中的值进行阴影警告?
F#值阴影 - 是否可以在同一范围内禁用值阴影
有没有办法通过编译器警告或在编辑器中可视化获得有关阴影变量的反馈.如何才能做到这一点?
当人们谈论F#时,他们有时会提到这个词top-level;
什么top-level意思?
例如在之前的SO Q&A中
错误FS0037有时,非常混乱
定义模块VS.NET与F#Interactive F#中
命名空间和模块之间的区别是什么?
F#
F#和MEF中的
AutoOpen属性:导出函数如何执行此F#函数
该术语也会在评论中定期出现,但对于那些问答我没有提及.
关于范围的维基百科文章涉及到这一点,但没有F#的具体细节.
F#3.x规范仅规定:
11.2.1.1函数和值的Arity一致性
括号表示一个
top-level函数,它可能是计算函数值的第一类计算表达式,而不是编译时函数值.
13.1自定义属性
例如,STAThread属性应该放在
top-level"do"语句之前.
14.1.8类型变量的名称解析
对于
top-level包含表达式和类型的任何成员或任何其他构造,它最初为空.
我怀疑这个术语在不同的语境中有不同的含义:范围,F#交互,阴影.
如果您还可以解释F#前身语言(ML,CAML,OCaml)的起源,我们将不胜感激.
最后,我不打算将答案标记为几天,以避免仓促答案.
我已经宣布了这样一个元组:
module MyModule =
let private INVALID_TUPLE = ("0", DateTime.MinValue)
Run Code Online (Sandbox Code Playgroud)
当我在模块中引用它时,它总是为null:
let private invalidForNone someOtherTuple =
match someOtherTuple with
| None -> INVALID_TUPLE // it's null
| Some(t) -> t
Run Code Online (Sandbox Code Playgroud)
此外,当我在元组声明上放置一个断点时,它永远不会命中.
如果我在脚本(fsx)文件中执行完全相同的操作,启动调试,执行,元组声明命中的断点以及对元组的引用是好的.
ILSpy for my module显示生成了一些启动代码,其中包含一个创建INVALID_TUPLE的Main方法.显然,这不是出于某种原因运行?
这是一个重现行为的示例(现在我意识到它与MSTest执行代码的方式有关).从C#单元测试中调用它; 结果将为null.实际上,F#代码中的断点根本不会执行.
module NullTupleTest
open System
let private INVALID_TUPLE = ("invalid", DateTime.MinValue)
let private TupleTest someTuple =
match someTuple with
| None -> INVALID_TUPLE
| Some(dt) -> dt
let Main = TupleTest None
Run Code Online (Sandbox Code Playgroud) f# ×7
haskell ×2
performance ×2
shadowing ×2
xml ×2
c# ×1
dictionary ×1
f#-data ×1
inline ×1
iso-prolog ×1
lambda ×1
libraries ×1
parsec ×1
prolog ×1
prolog-setof ×1
scope ×1
toplevel ×1
tuples ×1