35

在我的 TODO 中有新项目,无法选择 F# 或 Nemerle。

我目前正在学习 F#,并且在 Nemerle 上有一些项目。

我喜欢 F# 方式,我喜欢默认缩进(我也希望 nemerle2 默认缩进),我喜欢 F# 的许多特性和魔力,但没有宏。

F# 的目标是 VS2010,也许(也许)更大的开发团队,它看起来像 Haskell(可以用它创建轻量级的 Linux 程序,而且很有趣)。

Nemerle 的目标是宏,我想我更喜欢 Nemerle 的一些语法。

大多数人都喜欢 C#...

  • 所以语言必须是透视的
  • 语法必须合乎逻辑、易于理解、简洁且功能强大(代码少)
  • 它必须易于与 .NET 一起使用
  • 所以我已经用这三个的选择关闭了我的自我。
  • 我可以使用它们,但选择一个怎么样。这对我来说并不容易。

例如我喜欢(Nemerle)

match(STT)
    | 1 with st= "Summ"
    | 2 with st= "AVG" =>
        $"$st : $(summbycol(counter,STT))"

远不止于此 (F#)

let (|Let|) v e = (v, e)

match stt with 
| Let "Summ" (st, 1) 
| Let "AVG" (st, 2) -> srintf "%s ..." st

F# :

["A"; "B"] |> List.iter (fun s  -> printfn "%d"  s.Length)

内默尔:

["A", "B"].Iter(x => printf("%d", x.Length))

F#(希望这里没有弄错):

let type X =
    let mytable a = ""
    let mytable b = ""
    new(A, B) = {
    a <- A
    b <- B }
    member X.A 
        with get  = a
    member X.B 
        with get  = a

内默尔:

[Record]
class X
  public A : string { get; }
  public B : string { get; }

C# :

class X
{
    private readonly string _a;
    public string A { get { return _a; } }

    private readonly string _b;
    public string B { get { return _b; } }

    public X(string a, string b)
    {
        _a = a;
        _b = b;
    }
}

这是我已经无法转换为 F# 的 nemerle 代码(所以我只学习它)......

abstract class ABase
    abstract public A : string { get; }

interface IB
    B : string { get; }

[Record]
class My : ABase, IB
    public override A : string { get; }
    public virtual  B : string { get; }

与 C# 的比较:

abstract class ABase
{
    abstract public string A { get; }
}

interface IB
{
    string B { get; }
}

class My : ABase, IB
{
    private readonly string _a;
    public override A : string { get { return _a; } }

    private readonly string _b;
    public virtual  B : string { get { return _b; } }

    public My(string a, string b)
    {
        _a = a;
        _b = b;
    }
}

显然,Nemerle 代码更易于支持且更具可读性。

@Brian所以这就是我要问的原因,如果你发现我做错了,告诉我是否真的可以让 F#、C# 更轻松,因为我不确定其他方法可以清楚地做到这一点。

4

8 回答 8

15

如果您发现我做错了,请告诉我是否真的可以在 F# , C# 上使这个更轻松,因为我不确定其他方法可以清楚地做到这一点。

您的 F# 和 C# 示例不是很简洁。让我们重写 OP 中的一些示例:

模式匹配

内默尔:

match(STT)
    | 1 with st= "Summ"
    | 2 with st= "AVG" =>
        $"$st : $(summbycol(counter,STT))"

F#:

我不是 100% 确定你的代码在做什么,但它看起来像是基于这个 answer。我认为在匹配表达式中创建新变量并不容易,但我认为活动模式是多余的。

我会这样写代码:

let st = match stt with 1 -> "Summ" | 2 -> "Avg"
sprintf "%s ..." st

地图也可以:

let sttMap = [1, "Summ"; 2, "Avg"] |> Map.ofList
sprintf "%s ..." (sttMap.[stt])

我也是<3 乔恩的建议

let 1, st, _ | 2, _, st = stt, "Summ", "AVG"

记录

内默尔:

[Record]
class X
  public A : string { get; }
  public B : string { get; }

F#:

type X = { A : string; B : string }

C#:

class X {
    public string A { get; private set; }
    public string B { get; private set; }

    public X(string a, string b) {
        A = a;
        B = b;
    }
}

课程

内梅尔

abstract class ABase
    abstract public A : string { get; }

interface IB
    B : string { get; }

[Record]
class My : ABase, IB
    public override A : string { get; }
    public virtual  B : string { get; }

F#:

[<AbstractClass>]
type ABase() =
    abstract member A : string

type IB =
    abstract member B : string

type My(a, b) =
    inherit ABase()
    override this.A = a

    abstract member B : string
    default this.B = b
    interface IB with
        member this.B = this.B

这里需要注意的一些事项:

  • F# 接口是使用abstract关键字定义的。[<AbstractClass>]您可以使用该属性将它们转换为抽象类。

  • 接口是显式实现的。通常,您需要将对象强制转换为接口定义以调用接口成员:let x = My("a", "b"); printf "%s" (x :> IB).B. 为避免强制转换,您需要创建镜像接口方法的公共成员。

  • 虚函数定义了abstract member一个default实现。

你把所有这些组件放在一起,你就会得到对眼神经有害的类定义。但是没关系,因为类通常不经常使用。大多数 F# 对象模型是通过联合和记录定义的;在使用类地方,类层次结构非常扁平而不是很深,因此您看不到在 F# 中比在 C# 中经常使用的继承或虚函数。

C#:

abstract class ABase {
    public abstract string A { get; }
}

interface IB {
    string B { get; }
}

class My : ABase, IB {
    public override string A { get; private set; }
    public virtual string B { get; private set; }

    public My(string a, string b) {
        A = a;
        B = b;
    }
}

长话短说,我认为 F# 与 Nemerle 相当,但看起来你只是在学习它。别担心,当我学习 F# 时,我正在编写丑陋、笨重的代码,这些代码基本上反映了 C# 的语法,但语法更时髦。我花了一点时间才能写得更惯用。

我推荐以下内容:

  • 如果您熟悉 Nemerle 并喜欢使用它,请继续这样做 :)
  • 如果你想学习 F#,我认为你的项目是一个很好的起点。我认为您可以将 F# 编写得与 Nemerle 一样干净或更好。
  • C# 也可以,但如果您要进行大量模式匹配或符号处理,我不会更喜欢它而不是任何一种语言。
于 2011-01-03T19:19:56.963 回答
12

F# 和 Nemerle 版本完全不同:

  1. Nemerle 定义类,而 F# 定义结构。
  2. Nemerle 定义只读成员,而 F# 定义可变成员。
  3. Nemerle 定义公共属性,而 F# 定义公共字段。
  4. Nemerle 定义了构造函数,而 F# 没有定义它。

F# 示例的类似 Nemerle 代码如下:

struct X
  mutable A : string
  mutable B : string

第二个例子几乎相同:

  1. Nemerle 为 My 定义了构造函数,而 F# 没有定义它。
  2. Nemerle 定义了返回构造函数中传递的值的属性,而 F# 定义了具有常量值的属性。

Nemerle 版本比 F# 版本更短更清晰。

PS 关于花括号与缩进语法。Nemerle 支持这两种语法。

你可以写:

class M
{
  static Main() : void
  {
    Console.WriteLine("A");
  }
}

或使用缩进:

#pragma indent
class M
  static Main() : void
      Console.WriteLine("A");

甚至使用两种样式!

#pragma indent
class M
  static Main() : void { Console.WriteLine("A"); }
于 2010-12-30T11:08:24.703 回答
4

F# 记录的语法是

type X = { 
    A : string
    B : string 
}

至于您无法转换的代码

[<AbstractClass>]
type ABase() =
    abstract A : string

type IB = 
    abstract B : string

type MyClass(a, b) = 
    inherit ABase()
        override this.A = a
    interface IB with 
        member this.B = b

我不太确定您的模式匹配示例正在尝试做什么,但 F# 具有语法(我认为您正在寻找)

match (STT) with
    | 1 when st = "Sum" -> ...
    | 2 when st = "Avg" -> ...
于 2010-12-30T15:19:35.093 回答
3

F# 有更好的记录语法:

type X = struct
  val mutable A : string
  val mutable B : string
end

还有你的代码,转换为 F#:

[<AbstractClass>] 
type ABase = 
    abstract member A : string
    // you can declare default implementation using default keyword like this:
    // default this.A with get () = "default implementation"

type IB =
    interface
        abstract member B : string
    end

type My = class
    inherit ABase
        override my.A with get() = "A"
    interface IB with
        member my.B with get() = "B"
end
于 2010-12-30T10:26:21.180 回答
3

Sparkie 发布的代码转换对 C# 转换有点不忠实,因为 B 属性不是虚拟的,也没有直接暴露在 My 类型的实例上(您必须将实例转换为 IB 才能访问 B)。这是一个更忠实的转换:

[<AbstractClass>]
type ABase() =
    abstract A: string

type IB =
    abstract B: string

type My(a, b) =
    inherit ABase()

    override this.A = a

    abstract B: string
    default this.B = b

    interface IB with
        member this.B = this.B

如果您是 F# 新手,需要注意一件事:传递给接口成员实现的 this 引用属于声明类的类型,因此(在上面)IB 的 B 实现引用了在类中声明的 B 属性。

于 2011-01-03T18:21:51.633 回答
3

如果你喜欢这两种语言,为什么不混合它们呢?您可以在 Nemerle 中使用宏构建一些库,在 F# 中构建其他库。这不是使用托管运行时的优势之一吗?:D

然后,您可以使用更方便的语言(如 C# 或 VB.NET)构建 UI(如果有)。

一旦您决定为您的产品添加脚本支持,您就可以使用 IronPython/IronRuby!

我的理解是 Nemerle IDE 支持仅在 VS2008 中有效(但它支持 .net3.5 和 4.0),而 F# 在 VS2010 和 VS2008 中都有效,所以除了语法差异之外,它认为您的决定还需要考虑,哪个 IDE你将使用。

于 2011-05-27T12:24:28.017 回答
2

另一个比较。

使用关键字:

F#

use x = X()
use y = Y()
use z = Z()
...

内默尔:

using (o = SomeType())
  ...

using (x = X())
using (y = Y())
using (z = Z())
  ... 

字符串格式:

F#:

let a = 1
let b = "x"
sprintf "%d %s" (a + 1) b

内默尔:

def a = 1;
def b = "x";
def c = Nemerle.IO.sprintf("%d %s", a + 1, b);

字符串插值:

F#有这个功能吗?

内默尔:

def a = 1;
def b = "x";
def c = $"$(a + 1) - $b"; // 0 - x

foreach 关键字:F#:

let list1 = [ 1; 5; 100; 450; 788 ]
for i in list1 do
  printfn "%d" i

let list1 = [ 1; 5; 100; 450; 788 ]
Seq.iteri (printfn "%d - %d")

let list1 = [ 1; 5; 100; 450; 788 ]
Seq.iteri (fun i x -> if i > 10 then printfn "%d - %d" i x)

let list1 = [ 1; 5; 100; 450; 788 ]
for x in list1 do
  printf "%s" (if x > 10 then printf "A"
               elif x < 10 then printf "B"
               elif "C"

内默尔:

def list1 = [ 1, 5, 100, 450, 788 ]
foreach (i in list1)
  WriteLine($"$i")

def list1 = [ 1, 5, 100, 450, 788 ]
foreach (i in list1 with index) // index has an index of item. works for any collection.
  WriteLine($"$index - $i")

def list1 = [ 1, 5, 100, 450, 788 ]
foreach (i when i > 10 in list1 with index) // Pattern in foreach
  WriteLine($"$index - $i")

def list1 = [ 1, 5, 100, 450, 788 ]
foreach (_ in list1) // Implicit match
  | i when i > 10 => WriteLine("A")
  | i when i < 10 => WriteLine("B")
  | _ => WriteLine("C")

foreach (_ in [])
  | i when i > 10 => WriteLine("A")
  | i when i < 10 => WriteLine("B")
  | _ => WriteLine("C")
otherwise // Otherwise is taken when there were no iterations
  WriteLine("X")
于 2010-12-30T12:21:25.507 回答
2

“很明显,Nemerle 代码更易于支持且更具可读性。”

更容易支持谁?

谁更易读?

除了您自己的语言经验和您自己的个人偏见之外,您还根据什么做出判断?我已经用 C++ 编码多年,因此我发现任何不使用花括号来分隔代码块的语言都有点违反直觉。但是其他使用 Lisp(或基于 Lisp 的语言)编码的人可能会发现使用花括号的概念非常奇怪,因此违反直觉。使用 Pascal 或基于 Pascal 的语言编写代码的人宁愿看到“开始”和“结束”来分隔代码块——他们会发现“更易于支持且更具可读性”。

您是否有任何研究证明 Nemerle 语法的代码理解比 F# 或 C# 更高?因为那将是我能想到的唯一经验性、客观的衡量标准,它可以证明 Nemerle 代码“更容易支持和更易读”。

对于专业开发人员来说,这只是三种不同的语法。除非您将某种语言与Brainf*ck进行比较,否则基本上这只是一个问题,即您已经习惯了哪种语法以及您需要多长时间才能学习新的语法结构。

于 2010-12-30T14:59:35.087 回答