Daha yüksek dereceli işlev - Higher-order function

In matematik ve bilgisayar bilimleri , bir yüksek dereceden fonksiyonu bir olan fonksiyon aşağıdakilerden en az birini yapar:

  • argüman olarak bir veya daha fazla işlevi alır (yani prosedür parametreleri ),
  • sonucu olarak bir işlev döndürür.

Diğer tüm fonksiyonlar birinci dereceden fonksiyonlardır . Matematikte yüksek dereceli fonksiyonlar, operatörler veya fonksiyoneller olarak da adlandırılır . Diferansiyel operatör olarak hesap onun için bir işlev haritalar için, yaygın bir örneğidir türevi , aynı zamanda bir fonksiyonu. Yüksek mertebeden fonksiyonlar, matematikte "functor" kelimesinin diğer kullanımlarıyla karıştırılmamalıdır, bkz. Functor (anlam ayrımı) .

Türlenmemiş lambda hesabında , tüm fonksiyonlar daha yüksek mertebedendir; bir de yazılı lambda hesabı en başka, işlevsel programlama dilleri elde edilir, bağımsız değişken olarak bir fonksiyonu, daha yüksek dereceden fonksiyonlar formunun türleriyle değerleridir .

Genel örnekler

  • mapBirçok işlevsel programlama dilinde bulunan işlev, üst düzey bir işlevin bir örneğidir. Argüman olarak bir f fonksiyonunu ve bir eleman koleksiyonunu alır ve sonuç olarak koleksiyondaki her elemana f uygulanmış yeni bir koleksiyon döndürür .
  • Parametre olarak bir karşılaştırma işlevini alan ve programcının sıralama algoritmasını sıralanan öğelerin karşılaştırmalarından ayırmasına olanak tanıyan sıralama işlevleri. standart fonksiyonu qsort , bunun bir örneğidir.
  • filtre
  • katlamak
  • uygulamak
  • Fonksiyon bileşimi
  • Entegrasyon
  • Geri aramak
  • Ağaç geçişi
  • Doğal dilin anlamsal bir teorisi olan Montague dilbilgisi , daha yüksek mertebeden işlevleri kullanır.

Programlama dillerinde destek

Doğrudan destek

Örnekler, programlama dillerini karşılaştırmayı ve karşılaştırmayı amaçlamaz, ancak daha yüksek dereceli işlev sözdizimi örnekleri olarak hizmet eder.

Aşağıdaki örneklerde, üst düzey işlev twicebir işlev alır ve işlevi bir değere iki kez uygular. Eğer twiceaynı birkaç kez uygulanması gereken f, tercihen bir değer yerine bir işlev döndürmelidir. Bu, " kendini tekrar etme " ilkesine uygundur.

APL

      twice{⍺⍺ ⍺⍺ }

      plusthree{+3}

      g{plusthree twice }
    
      g 7
13

Veya zımni bir şekilde:

      twice2

      plusthree+3

      gplusthree twice
    
      g 7
13

C++

std::functionC++ 11'de kullanma :

#include <iostream>
#include <functional>

auto twice = [](const std::function<int(int)>& f)
{
    return [&f](int x) {
        return f(f(x));
    };
};

auto plus_three = [](int i)
{
    return i + 3;
};

int main()
{
    auto g = twice(plus_three);

    std::cout << g(7) << '\n'; // 13
}

Veya C++ 14 tarafından sağlanan genel lambdalarla:

#include <iostream>

auto twice = [](const auto& f)
{
    return [&f](int x) {
        return f(f(x));
    };
};

auto plus_three = [](int i)
{
    return i + 3;
};

int main()
{
    auto g = twice(plus_three);

    std::cout << g(7) << '\n'; // 13
}

C#

Yalnızca delegeleri kullanma:

using System;

public class Program
{
    public static void Main(string[] args)
    {
        Func<Func<int, int>, Func<int, int>> twice = f => x => f(f(x));

        Func<int, int> plusThree = i => i + 3;

        var g = twice(plusThree);

        Console.WriteLine(g(7)); // 13
    }
}

Veya eşdeğer olarak, statik yöntemlerle:

using System;

public class Program
{
    private static Func<int, int> Twice(Func<int, int> f)
    {
        return x => f(f(x));
    }

    private static int PlusThree(int i) => i + 3;

    public static void Main(string[] args)
    {
        var g = Twice(PlusThree);

        Console.WriteLine(g(7)); // 13
    }
}

Clojure

(defn twice [f]
  (fn [x] (f (f x))))

(defn plus-three [i]
  (+ i 3))

(def g (twice plus-three))

(println (g 7)) ; 13

ColdFusion İşaretleme Dili (CFML)

twice = function(f) {
    return function(x) {
        return f(f(x));
    };
};

plusThree = function(i) {
    return i + 3;
};

g = twice(plusThree);

writeOutput(g(7)); // 13

Ortak Lisp

(defun twice (f)                                                                
  (lambda (x) (funcall f (funcall f x))))                                       
                                                                                
(defun plus-three (i)                                                           
  (+ i 3))                                                                      
                                                                                
(defvar g (twice #'plus-three))                                                 
                                                                                
(print (funcall g 7))

NS

import std.stdio : writeln;

alias twice = (f) => (int x) => f(f(x));

alias plusThree = (int i) => i + 3;

void main()
{
    auto g = twice(plusThree);

    writeln(g(7)); // 13
}

Dart oyunu

int Function(int) twice(int Function(int) f) {
    return (x) {
        return f(f(x));
    };
}

int plusThree(int i) {
    return i + 3;
}

void main() {
    final g = twice(plusThree);
    
    print(g(7)); // 13
}

iksir

Elixir'de modül tanımlarını ve anonim işlevleri karıştırabilirsiniz.

defmodule Hof do
    def twice(f) do
        fn(x) -> f.(f.(x)) end
    end
end

plus_three = fn(i) -> 3 + i end

g = Hof.twice(plus_three)

IO.puts g.(7) # 13

Alternatif olarak, saf anonim işlevleri kullanarak da oluşturabiliriz.

twice = fn(f) ->
    fn(x) -> f.(f.(x)) end
end

plus_three = fn(i) -> 3 + i end

g = twice.(plus_three)

IO.puts g.(7) # 13

Erlang

or_else([], _) -> false;
or_else([F | Fs], X) -> or_else(Fs, X, F(X)).

or_else(Fs, X, false) -> or_else(Fs, X);
or_else(Fs, _, {false, Y}) -> or_else(Fs, Y);
or_else(_, _, R) -> R.

or_else([fun erlang:is_integer/1, fun erlang:is_atom/1, fun erlang:is_list/1], 3.23).

Bu Erlang örneğinde, üst düzey işlev or_else/2, işlevler ( Fs) ve bağımsız değişken ( X) listesini alır . Fonksiyonu Fargüman Xolarak argümanla değerlendirir. İşlev Ffalse döndürürse, sonraki işlev Fsdeğerlendirilir. İşlev Fdönerse , argümanlı {false, Y}sonraki işlev değerlendirilir. İşlev dönerse , üst düzey işlev döndürür . , , ve işlevleri olabileceğini unutmayın . Örnek döner . FsYFRor_else/2RXYRfalse

F#

let twice f = f >> f

let plus_three = (+) 3

let g = twice plus_three

g 7 |> printf "%A" // 13

Gitmek

package main

import "fmt"

func twice(f func(int) int) func(int) int {
	return func(x int) int {
		return f(f(x))
	}
}

func main() {
	plusThree := func(i int) int {
		return i + 3
	}

	g := twice(plusThree)

	fmt.Println(g(7)) // 13
}

Bir işlev değişmezinin bir tanımlayıcıyla ( twice) veya anonim olarak (değişkene atanmış olarak) tanımlanabileceğine dikkat edin plusThree.

Haskell

twice :: (Int -> Int) -> (Int -> Int)
twice f = f . f

plusThree :: Int -> Int
plusThree = (+3)

main :: IO ()
main = print (g 7) -- 13
  where
    g = twice plusThree

J

açıkça,

   twice=.     adverb : 'u u y'

   plusthree=. verb   : 'y + 3'
   
   g=. plusthree twice
   
   g 7
13

ya da zımnen,

   twice=. ^:2

   plusthree=. +&3
   
   g=. plusthree twice
   
   g 7
13

Java (1.8+)

Sadece işlevsel arayüzleri kullanarak:

import java.util.function.*;

class Main {
    public static void main(String[] args) {
        Function<IntUnaryOperator, IntUnaryOperator> twice = f -> f.andThen(f);

        IntUnaryOperator plusThree = i -> i + 3;

        var g = twice.apply(plusThree);

        System.out.println(g.applyAsInt(7)); // 13
    }
}

Veya eşdeğer olarak, statik yöntemlerle:

import java.util.function.*;

class Main {
    private static IntUnaryOperator twice(IntUnaryOperator f) {
        return f.andThen(f);
    }

    private static int plusThree(int i) {
        return i + 3;
    }

    public static void main(String[] args) {
        var g = twice(Main::plusThree);

        System.out.println(g.applyAsInt(7)); // 13
    }
}

JavaScript

"use strict";

const twice = f => x => f(f(x));

const plusThree = i => i + 3;

const g = twice(plusThree);

console.log(g(7)); // 13

Julia

julia> function twice(f)
           function result(x)
               return f(f(x))
           end
           return result
       end
twice (generic function with 1 method)

julia> plusthree(i) = i + 3
plusthree (generic function with 1 method)

julia> g = twice(plusthree)
(::var"#result#3"{typeof(plusthree)}) (generic function with 1 method)

julia> g(7)
13

Kotlin

fun twice(f: (Int) -> Int): (Int) -> Int {
    return { f(f(it)) }
}

fun plusThree(i: Int) = i + 3

fun main() {
    val g = twice(::plusThree)

    println(g(7)) // 13
}

Lua

function twice(f)
  return function (x)
    return f(f(x))
  end
end

function plusThree(i)
  return i + 3
end

local g = twice(plusThree)

print(g(7)) -- 13

MATLAB

function result = twice(f)
    result = @inner

    function val = inner(x)
        val = f(f(x));
    end
end

plusthree = @(i) i + 3;

g = twice(plusthree)

disp(g(7)); % 13

OCaml

let twice f x =
  f (f x)

let plus_three =
  (+) 3

let () =
  let g = twice plus_three in

  print_int (g 7); (* 13 *)
  print_newline ()

PHP

<?php

declare(strict_types=1);

function twice(callable $f): Closure {
    return function (int $x) use ($f): int {
        return $f($f($x));
    };
}

function plusThree(int $i): int {
    return $i + 3;
}

$g = twice('plusThree');

echo $g(7), "\n"; // 13

veya değişkenlerdeki tüm işlevlerle:

<?php

declare(strict_types=1);

$twice = fn(callable $f): Closure => fn(int $x): int => $f($f($x));

$plusThree = fn(int $i): int => $i + 3;

$g = $twice($plusThree);

echo $g(7), "\n"; // 13

Ok işlevlerinin üst kapsamdan gelen tüm değişkenleri örtük olarak yakaladığını, oysa anonim işlevlerin useanahtar kelimenin aynı şeyi yapmasını gerektirdiğini unutmayın .

Perl

use strict;
use warnings;

sub twice {
    my ($f) = @_;
    sub {
        $f->($f->(@_));
    };
}

sub plusThree {
    my ($i) = @_;
    $i + 3;
}

my $g = twice(\&plusThree);

print $g->(7), "\n"; # 13

veya değişkenlerdeki tüm işlevlerle:

use strict;
use warnings;

my $twice = sub {
    my ($f) = @_;
    sub {
        $f->($f->(@_));
    };
};

my $plusThree = sub {
    my ($x) = @_;
    $x + 3;
};

my $g = $twice->($plusThree);

print $g->(7), "\n"; # 13

piton

>>> def twice(f):
...     def result(x):
...         return f(f(x))
...     return result

>>> plusthree = lambda i: i + 3

>>> g = twice(plusthree)
    
>>> g(7)
13

Python dekoratör sözdizimi, genellikle bir işlevi daha yüksek dereceli bir işlevden geçirmenin sonucuyla değiştirmek için kullanılır. Örneğin, işlev geşdeğer olarak uygulanabilir:

>>> @twice
... def g(i):
...     return i + 3

>>> g(7)
13

r

twice <- function(f) {
  return(function(x) {
    f(f(x))
  })
}

plusThree <- function(i) {
  return(i + 3)
}

g <- twice(plusThree)

> print(g(7))
[1] 13

Raku

sub twice(Callable:D $f) {
    return sub { $f($f($^x)) };
}

sub plusThree(Int:D $i) {
    return $i + 3;
}

my $g = twice(&plusThree);

say $g(7); # 13

Raku'da, tüm kod nesneleri kapanışlardır ve bu nedenle sözcük değişkeni işlevin içinde "kapalı" olduğundan, bir dış kapsamdan iç "sözcüksel" değişkenlere başvurabilir. Raku ayrıca bir değişkene atanabilen veya anonim olarak çağrılan lambda ifadeleri için "sivri blok" sözdizimini de destekler.

yakut

def twice(f)
  ->(x) { f.call f.call(x) }
end

plus_three = ->(i) { i + 3 }

g = twice(plus_three)

puts g.call(7) # 13

Pas

fn twice(f: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 {
    move |x| f(f(x))
}

fn plus_three(i: i32) -> i32 {
    i + 3
}

fn main() {
    let g = twice(plus_three);

    println!("{}", g(7)) // 13
}

Skala

object Main {
  def twice(f: Int => Int): Int => Int =
    f compose f

  def plusThree(i: Int): Int =
    i + 3

  def main(args: Array[String]): Unit = {
    val g = twice(plusThree)

    print(g(7)) // 13
  }
}

Şema

(define (add x y) (+ x y))
(define (f x)
  (lambda (y) (+ x y)))
(display ((f 3) 7))
(display (add 3 7))

Bu Şema örneğinde, körlemeyi(f x) uygulamak için yüksek dereceli işlev kullanılır . Tek bir argüman alır ve bir fonksiyon döndürür. İfadenin değerlendirilmesi, değerlendirmeden sonra önce bir işlev döndürür . Döndürülen işlevdir . Ardından, argüman olarak 7 ile döndürülen işlevi değerlendirir ve 10 değerini döndürür. Bu, ifadenin eşdeğeridir , çünkü . ((f 3) 7)(f 3)(lambda (y) (+ 3 y))(add 3 7)(f x)(add x y)

Süratli

func twice(_ f: @escaping (Int) -> Int) -> (Int) -> Int {
    return { f(f($0)) }
}

let plusThree = { $0 + 3 }

let g = twice(plusThree)

print(g(7)) // 13

Tcl

set twice {{f x} {apply $f [apply $f $x]}}
set plusThree {{i} {return [expr $i + 3]}}

# result: 13
puts [apply $twice $plusThree 7]

Tcl, anonim bir işlev uygulamak için application komutunu kullanır (8.6'dan beri).

XACML

XACML standardı, bir işlevi birden çok öznitelik çantası değerine uygulamak için standartta daha yüksek dereceli işlevleri tanımlar.

rule allowEntry{
    permit
    condition anyOfAny(function[stringEqual], citizenships, allowedCitizenships)
}

XACML'deki üst düzey işlevlerin listesi burada bulunabilir .

XQuery

declare function local:twice($f, $x) {
  $f($f($x))
};

declare function local:plusthree($i) {
  $i + 3
};

local:twice(local:plusthree#1, 7) (: 13 :)

alternatifler

İşlev işaretçileri

C , C++ ve Pascal gibi dillerdeki işlev işaretçileri , programcıların işlevlere referanslar arasında geçiş yapmasına izin verir. Aşağıdaki C kodu, rastgele bir işlevin integralinin yaklaşık bir değerini hesaplar:

#include <stdio.h>

double square(double x)
{
    return x * x;
}

double cube(double x)
{
    return x * x * x;
}

/* Compute the integral of f() within the interval [a,b] */
double integral(double f(double x), double a, double b, int n)
{
    int i;
    double sum = 0;
    double dt = (b - a) / n;
    for (i = 0;  i < n;  ++i) {
        sum += f(a + (i + 0.5) * dt);
    }
    return sum * dt;
}

int main()
{
    printf("%g\n", integral(square, 0, 1, 100));
    printf("%g\n", integral(cube, 0, 1, 100));
    return 0;
}

Qsort C standart kütüphane işlevi daha yüksek seviyeli bir fonksiyonu davranışını taklit etmek için bir işlev işaretçisi kullanır.

makrolar

Makrolar , daha yüksek dereceli işlevlerin bazı etkilerini elde etmek için de kullanılabilir. Ancak makrolar, değişken yakalama probleminden kolayca kaçınamazlar; ayrıca, bir derleyicinin optimize etmesi daha zor olabilen, büyük miktarda yinelenen kodla sonuçlanabilir. Makrolar genellikle kesin olarak yazılmaz, ancak kesin olarak yazılmış kod üretebilirler.

Dinamik kod değerlendirmesi

Diğer zorunlu programlama dillerinde, değerlendirme kapsamında dinamik olarak kod yürüterek (bazen Eval veya Execute işlemleri olarak adlandırılır) daha yüksek dereceli işlevler aracılığıyla elde edilen aynı algoritmik sonuçların bazılarını elde etmek mümkündür . Bu yaklaşımın önemli dezavantajları olabilir:

  • Yürütülecek bağımsız değişken kodu genellikle statik olarak yazılmaz ; bu diller , yürütülecek kodun düzgün biçimliliğini ve güvenliğini belirlemek için genellikle dinamik yazmaya dayanır .
  • Argüman genellikle değeri çalışma zamanına kadar bilinmeyebilecek bir dize olarak sağlanır. Bu dize, program yürütme sırasında derlenmelidir ( tam zamanında derleme kullanılarak ) veya yorumlama yoluyla değerlendirilmelidir , bu da çalışma zamanında ek yüke neden olur ve genellikle daha az verimli kod üretir.

nesneler

In nesne yönelimli programlama daha yüksek mertebeden işlevlerini desteklemeyen diller, nesneleri etkili bir yedek olabilir. Bir nesnenin yöntemleri özünde işlevler gibi hareket eder ve bir yöntem, nesneleri parametre olarak kabul edebilir ve dönüş değerleri olarak nesneler üretebilir. Bununla birlikte, nesneler genellikle saf işlevlere kıyasla ek çalışma zamanı ek yükü taşır ve bir nesneyi ve yöntemini/yöntemlerini tanımlamak ve somutlaştırmak için ek ortak kod kodu içerir . Yığın tabanlı ( yığın tabanlı) nesnelere veya yapılara izin veren diller, bu yöntemle daha fazla esneklik sağlayabilir.

Bir işlev döndüren bir işlevle Free Pascal'da basit bir yığın tabanlı kayıt kullanma örneği :

program example;

type 
  int = integer;
  Txy = record x, y: int; end;
  Tf = function (xy: Txy): int;
     
function f(xy: Txy): int; 
begin 
  Result := xy.y + xy.x; 
end;

function g(func: Tf): Tf; 
begin 
  result := func; 
end;

var 
  a: Tf;
  xy: Txy = (x: 3; y: 7);

begin  
  a := g(@f);     // return a function to "a"
  writeln(a(xy)); // prints 10
end.

İşlev a(), bir Txykaydı girdi olarak alır ve kaydın xve yalanların (3 + 7) toplamının tamsayı değerini döndürür .

işlevsizleştirme

İşlevsizleştirme , birinci sınıf işlevlerden yoksun dillerde daha yüksek dereceli işlevleri uygulamak için kullanılabilir :

// Defunctionalized function data structures
template<typename T> struct Add { T value; };
template<typename T> struct DivBy { T value; };
template<typename F, typename G> struct Composition { F f; G g; };

// Defunctionalized function application implementations
template<typename F, typename G, typename X>
auto apply(Composition<F, G> f, X arg) {
    return apply(f.f, apply(f.g, arg));
}

template<typename T, typename X>
auto apply(Add<T> f, X arg) {
    return arg  + f.value;
}

template<typename T, typename X>
auto apply(DivBy<T> f, X arg) {
    return arg / f.value;
}

// Higher-order compose function
template<typename F, typename G>
Composition<F, G> compose(F f, G g) {
    return Composition<F, G> {f, g};
}

int main(int argc, const char* argv[]) {
    auto f = compose(DivBy<float>{ 2.0f }, Add<int>{ 5 });
    apply(f, 3); // 4.0f
    apply(f, 9); // 7.0f
    return 0;
}

Bu durumda, işlev aşırı yüklemesi yoluyla farklı işlevleri tetiklemek için farklı türler kullanılır . Bu örnekteki aşırı yüklenmiş işlevin imzası vardır auto apply.

Ayrıca bakınız

Referanslar