Aynı anda hem kör hem de varyasyon fonksiyonuna sahip olmak mümkün müdür?


13

Hem dinamik hem de fonksiyonel bir programlama dilinde körelme ve değişken işlevler sunmayı düşünüyorum, ancak bunun mümkün olup olmadığını merak ediyorum.

İşte bazı sözde kod:

sum = if @args.empty then 0 else @args.head + sum @args.tail

ki bu da onun tüm argümanlarını özetliyor. Sonra, sumkendisine bir sayı tedavi edilirse, sonuç olur 0. Örneğin,

sum + 1

+yalnızca sayılarda çalışabileceği varsayılarak 1'e eşittir . Bununla birlikte, sum == 0doğru bile olsa , sumkaç argüman verilirse verilsin değerini ve işlevsel özelliğini koruyacaktır (dolayısıyla "kısmen uygulanır" ve "değişken" aynı anda), örneğin, beyan edersem

g = sum 1 2 3

Sonra geşittir 6, ancak, yine de daha da uygulayabilirsiniz g. Örneğin g 4 5 == 15, doğrudur. Bu durumda, nesneyi gbir değişmezle değiştiremeyiz 6, çünkü bir tamsayı olarak işlendiğinde aynı değeri vermelerine rağmen, içinde farklı kodlar içerirler.

Bu tasarım gerçek bir programlama dilinde kullanılıyorsa, karışıklığa veya belirsizliğe neden olur mu?


1
Kesinlikle konuşmak gerekirse, köriyi bir dilin temeli olarak kullanmak, tüm işlevlerin tekli olmadığı anlamına gelir - sadece varyasyonlu işlevler değil, ikili işlevler bile yoktur! Bununla birlikte, o dildeki programlar hala çoklu argümanlar almış gibi görünecek ve bu da normal işlevler kadar değişken işlevler için de geçerli olacaktır.
Kilian Foth

O zaman sorum basitçe "bir nesne aynı anda hem fonksiyon hem de fonksiyon dışı bir değer olabilir mi?" Yukarıdaki örnekte, sumbir 0bağımsız değişken olmayan ve tekrar eden bir bağımsız değişken ile çağırır.
Michael Tsang

işi değil reducemi?
cırcır ucube

1
Eğer kullandığınız fonksiyonlara bir göz atın args: empty, head, ve tail. Bunların hepsi liste işlevleridir, belki daha kolay ve daha basit bir şeyin varyasyonlu şeylerin olacağı bir liste kullanmak olabileceğini düşündürmektedir. (Bunun sum [1, 2, 3]yerine sum 1 2 3)
Michael Shaw

Yanıtlar:


6

Varargs nasıl uygulanabilir? Argüman listesinin sonunu bildirmek için bir mekanizmaya ihtiyacımız var. Bu ya

  • özel bir sonlandırıcı değeri veya
  • vararg listesinin uzunluğu ekstra bir parametre olarak iletilir.

Bu mekanizmaların her ikisi de vararları uygulamak için körükleme bağlamında kullanılabilir, ancak doğru yazım önemli bir sorun haline gelir. Diyelim ki bir işlevle uğraştığımızı varsayalım sum: ...int -> int, ancak bu işlevin köri kullanması (aslında sum: int -> ... -> int -> intargüman sayısını bilmememiz dışında daha çok benzer bir tipimiz var ).

Durum: sonlandırıcı değeri: Izin vermek endözel sonlandırıcı ve Ttürü sum. Biz şimdi bu uygulanan biliyoruz end: işlev dönüşleri sum: end -> intve bu biz başka toplamı benzeri fonksiyonu olsun int uygulanan: sum: int -> T. Bu nedenle Tbu tür birliktir: T = (end -> int) | (int -> T). Yerine koyarak T, biz çeşitli olası tiplerini almak end -> int, int -> end -> int, int -> int -> end -> intvb Ancak, çoğu tip sistemler, türü için uygundur yoktur.

Durum: açık uzunluk: vararg işlevinin ilk argümanı varargs sayısıdır. Yani sum 0 : int, sum 1 : int -> int, sum 3 : int -> int -> int -> intvb Bu, bazı tip sistemlerde desteklenen ve bir örneğidir edilir bağımlı yazmaya . Aslında argüman sayısı bir tür parametre değil düzenli bir parametre olacaktır - bir çalışma zamanı değere bağlı için fonksiyonun Arity için anlamlı olmaz, s = ((sum (floor (rand 3))) 1) 2belli ki kötü yazılan: ya bu değerlendirir s = ((sum 0) 1) 2 = (0 1) 2, s = ((sum 1) 1) 2 = 1 2ya da s = ((sum 2) 1) 2 = 3.

Uygulamada, bu tekniklerin hiçbiri hataya yatkın olduklarından kullanılmamalıdır ve yaygın tip sistemlerde (anlamlı) bir türü yoktur. Bunun yerine, sadece bir Paramtre olarak değerlerinin bir listesini pass: sum: [int] -> int.

Evet, bir nesnenin hem fonksiyon hem de değer olarak görünmesi mümkündür, örneğin baskıya sahip bir tip sistemde. Izin sumbir olmak SumObjiki Coercions olan,:

  • coerce: SumObj -> int -> SumObjsumişlev olarak kullanılmasına izin verir ve
  • coerce: SumObj -> int sonucu çıkarmamızı sağlar.

Teknik olarak, bu, tür için bir sarıcı olan T = SumObjve bununla ilgili bir sonlandırıcı değer durumunun varyasyonudur coerce. Birçok nesne yönelimli dilde, bu, operatörün aşırı yüklenmesi ile önemsiz bir şekilde uygulanabilir, örn. C ++:

#include <iostream>
using namespace std;

class sum {
  int value;
public:
  explicit sum() : sum(0) {}
  explicit sum(int x) : value(x) {}
  sum operator()(int x) const { return sum(value + x); }  // function call overload
  operator int() const { return value; } // integer cast overload
};

int main() {
  int zero = sum();
  cout << "zero sum as int: " << zero << '\n';
  int someSum = sum(1)(2)(4);
  cout << "some sum as int: " << someSum << '\n';
}

Müthiş cevap! Listedeki vararları paketlemenin dezavantajı, kısmi köri uygulamasını kaybetmenizdir. ..., force=False)İlk işlevin uygulanmasını zorlamak için bir anahtar sözcük argümanı kullanarak sonlandırıcı yaklaşımınızın bir Python sürümü ile oynuyordum .
ThomasH

Bir liste alan bir işlevi kısmen uygulayan kendi üst düzey işlevinizi yapabilirsiniz curryList : ([a] -> b) -> [a] -> [a] -> b, curryList f xs ys = f (xs ++ ys).
Jack

2

Sen bakmak isteyebilirsiniz Haskell printf bu uygulamaya birlikte nasıl çalıştığını bu açıklama . İkinci sayfada Oleg Kiselyov'un bu tür bir şey yapma konusundaki makalesine bir bağlantı var, bu da okumaya değer. Aslında, işlevsel bir dil tasarlıyorsanız, Oleg'in web sitesi muhtemelen zorunlu okuma olmalıdır.

Kanımca, bu yaklaşımlar biraz hack, ama mümkün olduğunu gösteriyorlar. Bununla birlikte, dilinizde tam bağımlı yazma özelliği varsa, çok daha basittir. Tamsayı bağımsız değişkenlerini toplamak için değişken bir işlev daha sonra şöyle görünebilir:

type SumType = (t : union{Int,Null}) -> {SumType, if t is Int|
                                         Int,     if t is Null}
sum :: SumType
sum (v : Int) = v + sum
sum (v : Null) = 0

Özyinelemeli türü açık bir ad vermeden tanımlamak için bir soyutlama, bu tür işlevleri yazmayı kolaylaştırabilir.

Düzenleme: tabii ki, sadece soruyu tekrar okudum ve dinamik olarak yazılan bir dil söylediniz , bu noktada açıkça mekaniği gerçekten ilgili değil ve bu nedenle @ amon'un cevabı muhtemelen ihtiyacınız olan her şeyi içeriyor. Ah, statik bir dilde nasıl yapılacağını merak ederken herkesin karşılaşması durumunda bunu burada bırakacağım ...


0

Python'un isteğe bağlı argümanlarından yararlanarak, @amon'un "sonlandırıcı" yaklaşımını kullanan Python3'teki varyasyon fonksiyonlarının köreltilmesi için bir versiyon:

def curry_vargs(g):
    actual_args = []
    def f(a, force=False):
        nonlocal actual_args
        actual_args.append(a)
        if force:
            res = g(*actual_args)
            actual_args = []
            return res
        else:
            return f
    return f

def g(*args): return sum(args)
f = curry_vargs(g)
f(1)(2)(3)(4,True) # => 10

Döndürülen işlev f, dış kapsamda bağlı olan bir dizide ardışık çağrılarda kendisine iletilen bağımsız değişkenleri toplar. Yalnızca forcebağımsız değişken doğru olduğunda, orijinal işlev şu ana kadar toplanan tüm bağımsız değişkenlerle çağrılır.

Bu uygulamanın uyarıları her zaman ilk argümanı geçirmeniz gerektiğidir, fböylece bir "thunk", tüm argümanların bağlı olduğu ve sadece boş argüman listesiyle çağrılabilen bir işlev oluşturamazsınız (ancak bunun köri tipik uygulaması).

Başka bir uyarı, yanlış bir argümanı (örneğin yanlış türde) geçtikten sonra orijinal işlevi yeniden köri yapmanız gerektiğidir. Dahili diziyi sıfırlamanın başka bir yolu yoktur, bu yalnızca curried işlevinin başarılı bir şekilde yürütülmesinden sonra yapılır.

Sadeleştirilmiş sorunuz, "bir nesne aynı anda bir işlev ve bir işlev olmayan değer olabilir mi?", İç işlev nesnesine parantez olmadan bir işleve başvuru olarak Python uygulanabilir, bilmiyorum . Bu keyfi bir değer döndürmek için bükülmüş olabilir bilmiyorum.

Lisp sembollerinin aynı anda hem değeri hem de fonksiyon değeri olabileceğinden, Lisp'te muhtemelen kolay olurdu; fonksiyon değeri sembol fonksiyon pozisyonunda (bir listedeki ilk eleman olarak) göründüğünde kolayca seçilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.