9

我想知道,是否有任何编程语言可以进行这样的函数调用:

function_name(parameter1)function_name_continued(parameter2);

或者

function_name(param1)function_continued(param2)...function_continued(paramN);

例如你可以有这个函数调用:

int dist = distanceFrom(cityA)to(cityB);

如果你定义了distanceFromto这样的函数:

int distanceFrom(city A)to(city B)
{
   // find distance between city A and city B
   // ...
   return distance;
}

据我所知,在 C、Java 和 SML 编程语言中,这是做不到的。

您是否知道任何可以让您以这种方式定义和调用函数的编程语言?

4

16 回答 16

6

它看起来很像 Objective-C

- (int)distanceFrom:(City *)cityA to:(City *)cityB {
    // woah!
}
于 2010-07-22T10:46:39.403 回答
6

听起来很像 Smalltalk 的语法(这将解释 Objective-C 的语法 - 请参阅 kubi 的答案)。

例子:

dist := metric distanceFrom: cityA to: cityB

其中#distanceFrom:to: 是某个称为度量的对象上的方法。

所以你有“函数调用”(它们实际上是消息发送),比如

'hello world' indexOf: $o startingAt: 6. "$o means 'the character literal o"

编辑:我说过“真的,#distanceFrom:to: 应该在 City 类上称为 #distanceTo:,但无论如何。” 正义指出,这将城市与度量结合起来,这是不好的。您可能想要改变度量标准有充分的理由——飞机可能使用测地线,而汽车可能使用基于道路网络的最短路径。)

于 2010-07-22T10:48:44.043 回答
4

对于好奇的人,Agda2 有类似的、非常宽松的语法。以下是有效代码:

data City : Set where
  London : City
  Paris  : City

data Distance : Set where
  _km : ℕ → Distance

from_to_ : City → City → Distance
from London to London = 0 km
from London to Paris  = 342 km
from Paris  to London = 342 km
from Paris  to Paris  = 0 km

如果

from Paris to London

被评估,结果是

342 km
于 2010-07-22T11:15:12.817 回答
3

对我来说,它看起来很像一个流畅的接口链接方法

于 2010-07-22T11:18:06.223 回答
2

(请参阅我最喜欢的个人努力 - 此答案末尾的最终 C++ 方法)

语言一

Objective-C 但调用语法是 [object message] 所以看起来像:

int dist = [cities distanceFrom:cityA  to:cityB];

如果您城市对象中定义了distanceFromto这样的函数:

- (int)distanceFrom:(City *)cityA to:(City *)cityB 
  {
     // find distance between city A and city B
     // ...
     return distance;
  }

语言二

我还怀疑您可以在IO 语言中实现与此非常接近的东西,但我只是在看它。您可能还想阅读它与七周内七种语言中的其他语言的比较,其中有关于 IO的免费摘录。

语言三

根据C++ 的设计和演变,C++中有一个习惯用法(“链接”),您可以在其中返回临时对象或用于替换关键字参数的当前对象,如下所示:

int dist = distanceFrom(cityA).to(cityB);

如果你已经定义distanceFrom了这样的函数,带有一个小助手对象。请注意,内联函数使这种东西编译为非常有效的代码。

class DistanceCalculator
{
public:
    DistanceCalculator(City* from) : fromCity(from) {}

    int to(City * toCity) 
    {
         // find distance between fromCity and toCity
         // ...
         return distance;
    }

private:
    City* fromCity;
};


inline DistanceCalculator distanceFrom(City* from)
{
    return DistanceCalculator(from);
}

Duhh,我之前很着急,意识到我可以重构为只使用一个临时对象来提供相同的语法:

class distanceFrom
{
public:
    distanceFrom(City* from) : fromCity(from) {}

    int to(City * toCity) 
    {
         // find distance between fromCity and toCity
         // ...
         return distance;
    }

private:
    City* fromCity;
};

MY FAVORITE ,这是一个更具启发性的 C++ 版本,它允许您编写

int dist = distanceFrom cityA to cityB;

甚至

int dist = distanceFrom cityA to cityB to cityC;

基于#define 和类的奇妙 C++ ish 组合:

#include <vector>
#include <numeric>
class City;
#define distanceFrom DistanceCalculator() <<
#define to <<

class DistanceCalculator
{
public:

    operator int() 
    {
         // find distance between chain of cities
         return std::accumulate(cities.begin(), cities.end(), 0);
    }
    
    DistanceCalculator& operator<<(City* aCity)
    {
        cities.push_back(aCity);
        return *this;
    }

private:
    std::vector<City*> cities;
};

注意这可能看起来像一个无用的练习,但在某些情况下,用C++ 为人们提供一种与库一起编译的特定于领域的语言可能非常有用。我们为 CSIRO 的地理建模科学家使用了与 Python 类似的方法。

于 2010-07-22T11:06:13.867 回答
2

在 Python 中,您可以显式传递调用函数的参数的名称,这样您就可以以不同的顺序传递它们或跳过可选参数:

>>> l = [3,5,1,2,4]
>>> print l.sort.__doc__
L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*;
cmp(x, y) -> -1, 0, 1
>>> l.sort (reverse=True)
>>> l
[5, 4, 3, 2, 1]

这看起来很像 Objective C 语法正在做的事情,将每个参数标记为带有名称的函数。

于 2010-07-22T11:00:25.363 回答
2

C# 4.0 的命名参数和可选参数功能允许您实现非常相似的功能:

public static int Distance(string from, string to, string via = "")
{
   ...
}

public static void Main()
{
   int distance;

   distance = Distance(from: "New York", to: "Tokyo");
   distance = Distance(to: "Tokyo", from: "New York");
   distance = Distance(from: "New York", via: "Athens", to: "Tokyo");
}
于 2010-07-22T11:11:39.247 回答
1

您可以在 C 中执行此操作,尽管不安全:

struct Arg_s
    {
    int from;
    int to;
    };

int distance_f(struct Arg_s args)
    {
    return args.to - args.from;
    }

#define distance(...) distance_f( ((struct Arg_s){__VA_ARGS__}) )
#define from_ .from =
#define to_ .to =

使用复合文字指定的初始值设定项

printf("5 to 7 = %i\n",distance(from_ 5, to_ 7));
// 5 to 7 = 2
于 2010-08-02T01:45:58.090 回答
1

RemObjects 在其 Elements Compiler 中的 4 种联合语言中有 3 种在 OP 所要求的语法中具有此功能(以支持 Objective-C 运行时,但可用于所有操作系统)。

在 Hydrogene(扩展的 C#)中 https://docs.elementscompiler.com/Hydrogene/LanguageExtensions/MultiPartMethodNames

在碘(扩展 Java)中 https://docs.elementscompiler.com/Iodine/LanguageExtensions/MultiPartMethodNames

在 Oxygene(扩展 ObjectPascal)中,向下滚动到多部分方法名称部分 https://docs.elementscompiler.com/Oxygene/Members/Methods

于 2018-04-10T16:49:29.410 回答
0

好吧,在 Felix 中,您可以分两步实现:首先,您编写一个普通函数。然后,您可以扩展语法并将一些新的非终结符映射到函数。

与您可能想要的相比,这有点重量级(欢迎帮助使其更容易!)我认为这可以满足您的需求,而且还有更多!

我将举一个真实的例子,因为整个Felix 语言实际上是由这种技术定义的(下面的 x 是表达式的非终结符,x[p] 中的 p 是优先代码):

// alternate conditional
x[sdollar_apply_pri] := x[stuple_pri] "unless" x[let_pri] 
  "then" x[sdollar_apply_pri] =>#
  "`(ast_cond ,_sr ((ast_apply ,_sr (lnot ,_3)) ,_1 ,_5))";

这里还有一点:

// indexes and slices
x[sfactor_pri] := x[sfactor_pri] "." "[" sexpr "]" =># 
  "`(ast_apply ,_sr (,(noi   'subscript) (,_1 ,_4)))";
x[sfactor_pri] := x[sfactor_pri] "." "[" sexpr "to" sexpr "]" =># 
  "`(ast_apply ,_sr (,(noi 'substring) (,_1 ,_4 ,_6)))";
x[sfactor_pri] := x[sfactor_pri] "." "[" sexpr "to" "]" =># 
  "`(ast_apply ,_sr (,(noi 'copyfrom) (,_1 ,_4)))";
x[sfactor_pri] := x[sfactor_pri] "." "[" "to" sexpr "]" =># 
  "`(ast_apply ,_sr (,(noi 'copyto) (,_1 ,_5)))";

Felix 语法是普通的用户代码。在示例中,语法动作是用 Scheme 编写的。语法是 GLR。它允许“上下文敏感关键字”,即仅在某些上下文中作为关键字的标识符,这使得发明新结构变得容易,而不必担心破坏现有代码。

也许您想检查Felix Grammar Online

于 2011-12-25T23:41:37.207 回答
0

Tcl 允许你做这样的事情:

proc distance {from cityA to cityB} {...}
set distance [distance from "Chicago IL" to "Tulsa OK"]

我不确定这是否就是你所想的。

于 2010-07-22T13:37:57.640 回答
0

这看起来类似于函数重载 (C++/C#)/默认参数 (VB)。

默认参数允许定义函数的人为后面的参数设置默认值:

例如 c# 重载:

int CalculateDistance(city A, city B, city via1, city via2) 
{....}

int CalculateDistance(city A, city B) 
{
  return CalculateDistance(city A, city B, null, null)
}
于 2010-07-22T10:51:49.987 回答
0

在 SML 中,您可以简单地将“to”设置为某个值(例如,单位),并将“distanceFrom”设置为采用三个参数的柯里化函数。例如:

val to = ()
fun distanceFrom x _ y = (* implementation function body *)

val foo = distanceFrom cityA to cityB

您还可以利用 SML 不对数据类型构造函数强制执行命名约定这一事实(这让很多人感到烦恼),因此如果您想确保类型系统强制执行您的自定义语法:

datatype comp = to

fun distanceFrom x to y = (* implementation *)

val foo = distanceFrom cityA to cityB (* works *)
val foo' = distanceFrom cityA cityB (* whoops, forgot 'to' - type error! *)
于 2010-07-22T11:10:13.747 回答
0

您可以为此使用成员函数。

cityA.distance_to(cityB);

这是 C++、C(稍作调整)、C#、Java 中的有效代码。使用方法链,您可以:

cityA.something(cityB).something(cityC).something(cityD).something(cityE);
于 2010-07-22T10:58:11.030 回答
0

您可以使用宏在 Scheme 或 LISP 中执行此操作。

表格将类似于:

(DISTANCE-FROM city-a TO city-b)

大写的符号表示语法。

您甚至可以执行“命名参数”之类的操作:

(DISTANCE TO city-a FROM city-b)
(DISTANCE FROM city-a TO city-b)
于 2010-07-22T11:18:03.853 回答
0

你可以在Java中做到这一点,使用Joshua Bosch 的《 Effective Java》一书中出现的Builder 模式 (这是我第二次将此链接放入 SO,我仍然没有使用该模式,但看起来很棒)

于 2010-08-04T19:17:41.883 回答