【问题标题】:Any programming language with "strange" function call?任何具有“奇怪”函数调用的编程语言?
【发布时间】:2011-03-19 11:42:12
【问题描述】:

我想知道,有没有可以像这样调用函数的编程语言:

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 编程语言中,这是做不到的。

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

【问题讨论】:

  • 您能否具体说明一下这将实现什么?
  • 嗯,我认为某些函数调用会更容易阅读,例如“distanceFromto”示例。但我只是好奇是否存在这样一种让你定义这样的函数的编程语言。
  • 我认为您需要一个更好的示例来说明您要说的内容,因为将多个值传递给函数可以实现相同的目的并且同样(如果不是更多)清晰。例如。 dist = distanceFrom(cityA, cityB)

标签: programming-languages function function-calls


【解决方案1】:

它看起来很像 Objective-C

- (int)distanceFrom:(City *)cityA to:(City *)cityB {
    // woah!
}

【讨论】:

  • 感谢您的回答。我猜,你写的代码就是函数定义。能否举个例子说明这个函数是如何调用的?
  • 几乎与 Smalltalk 相同:dist = [metric distanceFrom: cityA to: cityB];
【解决方案2】:

听起来很像 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:,但无论如何。” Justice 指出这将城市与度量结合起来,这是不好的。您可能想要改变指标有充分的理由 - 飞机可能使用测地线,而汽车可能使用基于道路网络的最短路径。)

【讨论】:

  • 度量标准是strategy,用于测量两点之间的距离。有多种有效策略,返回不同的有效结果。 metric#distanceFrom:to 是正确的技术; city#distanceTo 充其量只是一个近似值。城市对与其他城市的距离了解多少?!城市知道自己的位置,指标知道衡量两个位置之间的距离。
  • 正义,你是对的。我喜欢#distanceTo:,因为它很短——但这是一个可能不值得做的权衡,因为它增加了耦合(城市知道非城市的事情),而且它并不比#distanceFrom:to:短多少。谢谢!
【解决方案3】:

这看起来类似于函数重载 (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)
}

【讨论】:

    【解决方案4】:

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

    cityA.distance_to(cityB);
    

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

    cityA.something(cityB).something(cityC).something(cityD).something(cityE);
    

    【讨论】:

      【解决方案5】:

      在 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 语法正在做的事情,将每个参数标记为带有名称的函数。

      【讨论】:

        【解决方案6】:

        (请参阅我最喜欢的个人努力 - 此答案末尾的最终 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 Language 中实现非常接近此的目标,但我只是在看它。您可能还想了解它与 Seven Languages in Seven Weeks 中的其他语言的比较,其中 free excerpt 关于 IO。

        第三语言

        根据The Design and Evolution of 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;
        };
        

        我的最爱 这是一个更具启发性的 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 类似的方法。

        【讨论】:

        • 我不了解 C++,但作业:int dist = distanceFrom cityA to cityB; 看起来不错!感谢您花时间回答我的问题。
        • 正是这种关于 C++ 的聪明才智总是让我既微笑又畏缩。
        【解决方案7】:

        在 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! *)
        

        【讨论】:

        • 虽然我不是在寻找咖喱函数。我真的觉得你用datatype comp = to 做的把戏很有趣而且很好:)(为此+1)
        【解决方案8】:

        C# 4.0 的 Named and Optional Arguments 功能允许您实现非常相似的目标:

        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");
        }
        

        【讨论】:

        • 这和Common Lisp的关键字参数太像了,吓人:(距离:“纽约”到:“东京”)
        • 关键字以:开头,不以(distance :from "New York" :to "Tokyo")结尾。但是,是的。
        • Python 也有关键字参数。
        【解决方案9】:

        对于好奇的人,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
        

        【讨论】:

          【解决方案10】:

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

          表单将类似于:

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

          大写的符号表示语法。

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

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

          【讨论】:

          • 你可以很高兴地拥有 (distance :from 'city-a :to 'city-b) 这个:(defun distance (&key from to) ...) (但这并没有解决问题,因为该函数仍称为 DISTANCE)。
          • @functional 我不知道我在想什么。你不能用 C 处理器来做到这一点。它必须在标记化级别完成,即 Lisp 宏操作的级别。
          • @functional,@reinierpost,我很确定你可以,使用compound literalsdesignated initializers
          【解决方案11】:

          在我看来很像 fluent interfacemethod chaining

          【讨论】:

            【解决方案12】:

            Tcl 允许你做这样的事情:

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

            我不确定这是否正是您的想法。

            【讨论】:

              【解决方案13】:

              您可以在 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 =
              

              使用compound literalsdesignated initializers

              printf("5 to 7 = %i\n",distance(from_ 5, to_ 7));
              // 5 to 7 = 2
              

              【讨论】:

              • 俗话说:可以,不代表应该。
              【解决方案14】:

              您可以在 Java 中完成,使用 Joshua Bosch 的 Effective Java 一书中出现的 Builder pattern(这是第二次我把这个链接放在 SO,我仍然没有使用那个模式,但看起来很棒)

              【讨论】:

                【解决方案15】:

                嗯,在 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

                【讨论】:

                  【解决方案16】:

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

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

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

                  在 Oxygene(一个扩展的 ObjectPascal)中,向下滚动到 Multi-Part Method Names 部分 https://docs.elementscompiler.com/Oxygene/Members/Methods

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-06-24
                    • 1970-01-01
                    • 2020-07-24
                    • 1970-01-01
                    • 2018-12-09
                    • 1970-01-01
                    相关资源
                    最近更新 更多