Julia: Perangkaian fungsi

Dibuat pada 27 Jan 2014  ·  232Komentar  ·  Sumber: JuliaLang/julia

Apakah mungkin untuk mengizinkan pemanggilan fungsi apa pun pada Any sehingga nilai diteruskan ke fungsi sebagai parameter pertama dan parameter yang diteruskan ke pemanggilan fungsi pada nilai ditambahkan setelahnya?
ex.

sum(a::Int, b::Int) -> a + b

a = 1
sum(1, 2) # = 3
a.sum(2) # = 3 or
1.sum(2) # = 3

Apakah mungkin untuk menunjukkan dengan cara deterministik fungsi apa yang akan dikembalikan untuk menghindari pengecualian waktu proses?

Komentar yang paling membantu

Jadi daftar kami saat ini dari berbagai upaya dll
Saya pikir ada baiknya orang memeriksa ini, (idealnya sebelum berpendapat, tapi w / e)
semuanya sedikit berbeda.
(Saya mencoba untuk memesan secara kronologis).

Paket

Prototipe Non-paket

Terkait:


Mungkin ini harus diedit ke salah satu posting teratas.

diperbarui: 2020-04-20

Semua 232 komentar

Sintaks . sangat berguna, jadi kami tidak akan menjadikannya hanya sinonim untuk pemanggilan fungsi. Saya tidak mengerti keuntungan dari 1.sum(2) lebih dari sum(1,2) . Bagi saya, hal itu tampaknya membingungkan.

Apakah pertanyaan tentang pengecualian merupakan masalah yang terpisah? saya rasa jawabannya tidak, selain membungkus tubuh fungsi di try..catch.

Contoh 1.sum (2) itu sepele (saya juga lebih suka jumlah (1,2)) tetapi ini hanya untuk menunjukkan bahwa suatu fungsi tidak dimiliki per se oleh tipe itu misalnya. 1 bisa diteruskan ke fungsi dengan parameter pertama adalah Real, tidak hanya ke fungsi yang mengharapkan parameter pertama menjadi Int.

Sunting: Saya mungkin telah salah memahami komentar Anda. Fungsi titik akan berguna saat menerapkan pola desain tertentu seperti pola pembangun yang biasa digunakan untuk konfigurasi. ex.

validate_for(name).required().gt(3) 
# vs 
gt(required(validate_for(name)), 3) 

Pengecualian yang baru saja saya maksud adalah karena fungsi yang mengembalikan hasil non-deterministik (yang bagaimanapun juga merupakan praktik yang buruk). Contoh akan memanggil a.sum (2) .sum (4) di mana .sum (2) terkadang mengembalikan String alih-alih Int tetapi .sum (4) mengharapkan Int. Saya menganggap compiler / runtime sudah cukup pintar untuk mengevaluasi keadaan seperti itu - yang akan sama ketika menyusun jumlah fungsi (sum (1, 2), 4) - tetapi permintaan fitur akan memerlukan perluasan fungsionalitas tersebut untuk memberlakukan batasan jenis pada fungsi titik.

Salah satu kasus penggunaan yang disukai orang adalah "antarmuka yang lancar". Terkadang bagus di OOP API ketika metode mengembalikan objek, sehingga Anda dapat melakukan hal-hal seperti some_obj.move(4, 5).scale(10).display()

Bagi saya, saya pikir ini lebih baik diekspresikan sebagai komposisi fungsi, tetapi |> tidak berfungsi dengan argumen kecuali Anda menggunakan anon. fungsi, misalnya some_obj |> x -> move(x, 4, 5) |> x -> scale(x, 10) |> display , yang cukup jelek.

Salah satu pilihan untuk mendukung hal semacam ini adalah jika |> mendorong LHS sebagai argumen pertama ke RHS sebelum mengevaluasi, tetapi kemudian tidak dapat diimplementasikan sebagai fungsi sederhana seperti sekarang.

Pilihan lain adalah semacam makro @composed yang akan menambahkan perilaku semacam ini ke ekspresi berikut

Anda juga dapat mengalihkan tanggung jawab untuk mendukung ini ke desainer perpustakaan, di mana mereka dapat menentukan

function move(obj, x, y)
    # move the object
end

move(x, y) = obj -> move(obj, x, y)

jadi ketika Anda tidak menyediakan objek, ia melakukan aplikasi fungsi parsial (dengan mengembalikan fungsi dari 1 argumen) yang kemudian dapat Anda gunakan di dalam rantai normal |> .

Sebenarnya, definisi |> mungkin bisa diubah sekarang menjadi
perilaku yang Anda minta. Saya akan mendukungnya.

Pada hari Senin, 27 Januari 2014, Spencer Russell [email protected]
menulis:

Salah satu kasus penggunaan yang disukai orang adalah "antarmuka yang lancar". Nya
terkadang bagus di OOP API ketika metode mengembalikan objek, jadi Anda bisa melakukannya
hal-hal seperti some_obj.move (4, 5) .scale (10) .display ()

Bagi saya, saya pikir ini lebih baik diekspresikan sebagai komposisi fungsi, tetapi
|> tidak bekerja dengan argumen kecuali Anda menggunakan anon. fungsi, misalnya some_obj
|> x -> pindah (x, 4, 5) |> x -> skala (x, 10) |> tampilan, yang cantik
jelek.

Salah satu pilihan untuk mendukung hal semacam ini adalah jika |> mendorong LHS sebagai
argumen pertama ke kanan sebelum evaluasi, tapi kemudian tidak bisa
diimplementasikan sebagai fungsi sederhana seperti sekarang.

Opsi lain adalah semacam makro @ tersusun yang akan menambahkan ini
semacam perilaku ke ekspresi berikut

Anda juga dapat mengalihkan tanggung jawab untuk mendukung ini ke perpustakaan
desainer, di mana mereka bisa mendefinisikan

perpindahan fungsi (obj, x, y)
# pindahkan objek
akhir

pindah (x, y) = obj -> pindah (obj, x, y)

jadi ketika Anda tidak menyediakan objek itu melakukan aplikasi fungsi parsial
(dengan mengembalikan fungsi dari 1 argumen) yang kemudian bisa Anda gunakan di dalam a
normal |> rantai.

-
Balas email ini secara langsung atau lihat di Gi tHubhttps: //github.com/JuliaLang/julia/issues/5571#issuecomment -33408448
.

ssfrr Saya suka cara Anda berpikir! Saya tidak menyadari komposisi fungsi |> . Saya melihat baru-baru ini ada diskusi serupa [https://github.com/JuliaLang/julia/issues/4963].

kmsquire Saya menyukai gagasan untuk memperluas komposisi fungsi saat ini untuk memungkinkan Anda menentukan parameter pada fungsi pemanggilan ex. some_obj |> move(4, 5) |> scale(10) |> display . Dukungan asli berarti satu penutupan yang lebih sedikit, tetapi apa yang disarankan ssfrr adalah cara yang layak untuk saat ini dan sebagai manfaat tambahan, dukungan tersebut juga harus kompatibel dengan fungsionalitas komposisi fungsi yang diperluas jika diterapkan.

Terima kasih atas tanggapan yang cepat :)

Sebenarnya, @ssfrr benar - tidak mungkin mengimplementasikan ini sebagai fungsi sederhana.

Yang Anda inginkan adalah threading makro (mis. Http://clojuredocs.org/clojure_core/clojure.core/-%3E). Sayangnya @ -> @ - >> @ -? >> bukan sintaks yang layak di Julia.

Ya, saya berpikir bahwa makro infix akan menjadi cara untuk mengimplementasikan ini. Saya tidak cukup paham dengan makro untuk mengetahui apa saja batasannya.

Saya pikir ini berfungsi untuk makro penulisan @ssfrr :

Sunting: Ini mungkin sedikit lebih jelas:

import Base.Meta.isexpr
_ispossiblefn(x) = isa(x, Symbol) || isexpr(x, :call)

function _compose(x)
    if !isa(x, Expr)
        x
    elseif isexpr(x, :call) &&    #
        x.args[1] == :(|>) &&     # check for `expr |> fn`
        length(x.args) == 3 &&    # ==> (|>)(expr, fn)
        _ispossiblefn(x.args[3])  #

        f = _compose(x.args[3])
        arg = _compose(x.args[2])
        if isa(f, Symbol)
            Expr(:call, f, arg) 
        else
            insert!(f.args, 2, arg)
            f
        end
    else
        Expr(x.head, [_compose(y) for y in x.args]...)
    end
end

macro compose(x)
    _compose(x)
end
julia> macroexpand(:(<strong i="11">@compose</strong> x |> f |> g(1) |> h('a',"B",d |> c(fred |> names))))
:(h(g(f(x),1),'a',"B",c(d,names(fred))))

Jika kita akan memiliki sintaks |> , saya pasti akan membuatnya lebih berguna daripada sekarang. Menggunakan hanya untuk memungkinkan menempatkan fungsi untuk diterapkan di kanan dan bukan di kiri selalu tampak seperti pemborosan sintaksis yang sangat besar.

+1. Ini sangat penting saat Anda menggunakan Julia untuk analisis data, di mana Anda biasanya memiliki pipeline transformasi data. Secara khusus, Panda dengan Python nyaman digunakan karena Anda dapat menulis hal-hal seperti df.groupby ("sesuatu"). Aggregate (sum) .std (). Reset_index (), yang merupakan mimpi buruk untuk menulis dengan sintaks |> saat ini .

: +1: untuk ini.

(Saya sudah berpikir untuk menyarankan penggunaan operator infix .. untuk ini ( obj..move(4,5)..scale(10)..display ), tetapi operator |> akan bagus juga)

Kemungkinan lain adalah menambahkan gula sintaksis untuk kari, seperti
f(a,~,b) menerjemahkan ke x->f(a,x,b) . Kemudian |> dapat mempertahankan artinya saat ini.

Oooh, itu akan menjadi cara yang sangat bagus untuk mengubah ekspresi apa pun menjadi fungsi.

Mungkin sesuatu seperti literal fungsi anonim Clojure, di mana #(% + 5) adalah singkatan dari x -> x + 5 . Ini juga menggeneralisasi beberapa argumen dengan% 1,% 2, dll. Jadi #(myfunc(2, %1, 5, %2) adalah singkatan dari x, y -> myfunc(2, x, 5, y)

Secara estetika saya tidak berpikir bahwa sintaks sangat cocok dengan julia jika tidak sangat mudah dibaca, tapi saya suka ide umumnya.

Untuk menggunakan contoh saya di atas (dan beralih ke @malmaud 's tilde bukan%), Anda bisa melakukannya

some_obj |> move(~, 4, 5) |> scale(~, 10) |> display

yang terlihat cukup bagus.

Ini bagus karena tidak memberikan perlakuan khusus pada argumen pertama. Sisi negatifnya adalah bahwa dengan cara ini kita mengambil simbol.

Mungkin ini adalah tempat lain di mana Anda bisa menggunakan makro, jadi substitusi hanya terjadi dalam konteks makro.

Kami jelas tidak dapat melakukan ini dengan ~ karena itu sudah menjadi fungsi standar di Julia. Scala melakukan ini dengan _ , yang juga dapat kita lakukan, tetapi ada masalah signifikan dengan mencari tahu bagian ekspresi mana yang merupakan fungsi anonim. Sebagai contoh:

map(f(_,a), v)

Yang mana artinya ini?

map(f(x->x,a), v)
map(x->f(x,a), v)
x->map(f(x,a), v)

Semuanya adalah interpretasi yang valid. Sepertinya saya ingat bahwa Scala menggunakan tanda tangan tipe dari fungsi untuk menentukan ini, yang menurut saya sangat disayangkan karena itu berarti Anda tidak dapat benar-benar mengurai Scala tanpa mengetahui jenis semuanya. Kami tidak ingin melakukan itu (dan bahkan tidak bisa jika kami menginginkannya), jadi harus ada aturan sintaksis murni untuk menentukan makna mana yang dimaksudkan.

Benar, saya mengerti maksud Anda tentang ambiguitas seberapa jauh harus keluar. Di Clojure, seluruh ekspresi dibungkus dalam #(...) jadi tidak ambigu.

Dalam Julia, apakah idiomatis menggunakan _ sebagai nilai tidak peduli? Seperti x, _ = somfunc() jika somefunc mengembalikan dua nilai dan Anda hanya ingin yang pertama?

Untuk mengatasinya saya pikir kita membutuhkan makro dengan penggunaan seperti interpolasi:

some_obj |> @$(move($, 4, 5)) |> @$(scale($, 10)) |> display

tetapi sekali lagi, saya pikir itu menjadi sangat berisik pada saat itu, dan saya tidak berpikir bahwa @$(move($, 4, 5)) memberi kita apa pun atas sintaks yang ada x -> move(x, 4, 5) , yang IMO lebih cantik dan lebih eksplisit.

Saya pikir ini akan menjadi aplikasi yang bagus dari makro infiks. Seperti dengan # 4498, jika aturan apa pun yang mendefinisikan fungsi sebagai infiks yang juga diterapkan ke makro, kita dapat memiliki makro @-> atau @|> yang akan memiliki perilaku penguliran.

Ya, saya suka ide makro infix, meskipun operator baru bisa diperkenalkan untuk penggunaan ini sebagai pengganti memiliki seluruh sistem untuk makro di tempat. Sebagai contoh,
some_obj ||> move($,4,5) ||> scale($, 10) |> disp
atau mungkin hanya menyimpan |> tetapi memiliki aturan itu
x |> f secara implisit berubah menjadi x |> f($) :
some_obj |> scale($,10) |> disp

Teman-teman, semuanya terlihat jelek: |> ||> dll.
Sejauh ini saya menemukan sintaks Julia sangat jelas sehingga hal-hal yang dibahas di atas tidak terlihat begitu cantik jika dibandingkan dengan hal lain.

Di Scala itu mungkin yang terburuk - mereka memiliki begitu banyak operator seperti ::,:, <<, >> + :: dan seterusnya - itu hanya membuat kode menjadi jelek dan tidak dapat dibaca untuk seseorang tanpa pengalaman beberapa bulan dalam menggunakan bahasa.

Sayang sekali Anda tidak menyukai lamaran itu, anton. Akan sangat membantu jika Anda membuat proposal alternatif.

Oh maaf, saya tidak mencoba untuk menjadi tidak baik. Dan ya - kritikus tanpa proposal
tidak berguna.

Sayangnya saya bukan seorang ilmuwan yang membuat bahasa jadi saya tidak melakukannya
tahu apa yang harus diusulkan ... yah, kecuali metode pembuatan yang dimiliki secara opsional
objek seperti dalam beberapa bahasa.

Saya suka frase "ilmuwan membangun bahasa" - kedengarannya jauh lebih muluk daripada programmer numerik yang muak dengan Matlab.

Saya merasa bahwa hampir setiap bahasa memiliki cara untuk menghubungkan fungsi - baik dengan aplikasi berulang . dalam bahasa OO, atau sintaks khusus hanya untuk tujuan itu dalam bahasa yang lebih fungsional (Haskell, Scala, Mathematica, dll.). Bahasa-bahasa terakhir itu juga memiliki sintaks khusus untuk argumen fungsi anonim, tetapi menurut saya Julia tidak akan benar-benar pergi ke sana.

Saya akan mengulangi dukungan untuk proposal Spencer - x |> f(a) diterjemahkan ke dalam f(x, a) , sangat mirip dengan cara kerja do blok (dan ini memperkuat tema umum bahwa argumen pertama dari sebuah fungsi istimewa di Julia untuk tujuan gula sintaksis). x |> f kemudian dilihat sebagai short-hand untuk x |> f() . Sederhana, tidak memperkenalkan operator baru, menangani sebagian besar casing yang kami inginkan untuk fungsi chaining, kompatibel ke belakang, dan sesuai dengan prinsip desain Julia yang ada.

Saya juga berpikir itu adalah proposal terbaik di sini, masalah utamanya adalah tampaknya menghalangi pendefinisian |> untuk hal-hal seperti pengalihan I / O atau tujuan khusus lainnya.

Sebagai catatan, . bukanlah sintaks rangkaian fungsi khusus, tetapi berfungsi seperti itu jika fungsi di sebelah kiri mengembalikan objek yang baru saja dimodifikasi, yang merupakan sesuatu yang harus dilakukan pengembang perpustakaan dengan sengaja.

Secara analogi, di Julia, pengembang perpustakaan sudah dapat mendukung rantai dengan |> dengan mendefinisikan fungsinya dari argumen N untuk mengembalikan fungsi dari 1 argumen saat diberikan argumen N-1, seperti yang disebutkan di sini

Tampaknya akan menimbulkan masalah jika Anda _ ingin_ fungsi Anda mendukung jumlah variabel dari args, jadi memiliki operator yang dapat melakukan penjejalan argumen akan lebih baik.

@JeffBezanson , sepertinya operator ini bisa diimplementasikan jika ada cara untuk melakukan infix macro. Apakah Anda tahu jika ada masalah ideologis dengan itu, atau tidak diterapkan?

Baru-baru ini, ~ menggunakan kasing khusus sehingga mengutip argumen dan panggilannya
makro @~ secara default. |> dapat dibuat untuk melakukan hal yang sama.

Tentu saja, dalam beberapa bulan, seseorang akan meminta <| untuk melakukan hal yang sama ...

Pada hari Kamis, 6 Februari 2014, Spencer Russell [email protected]
menulis:

Sekadar catatan,. bukan sintaks rangkaian fungsi khusus, tetapi ini terjadi
untuk bekerja seperti itu jika fungsi di sebelah kiri mengembalikan objek itu
diubah, yang harus dilakukan oleh pengembang perpustakaan
dengan sengaja.

Secara analogi, di Julia, pengembang perpustakaan sudah dapat mendukung rantai
dengan |> dengan mendefinisikan fungsinya dari argumen N untuk mengembalikan fungsi
dari 1 argumen ketika diberikan argumen N-1, seperti yang disebutkan di sini

Tampaknya akan menimbulkan masalah jika Anda _ ingin_ mendukung fungsi Anda
jumlah variabel args, bagaimanapun, jadi memiliki operator yang dapat melakukan
isian argumen akan menyenangkan.

@JeffBezanson https://github.com/JeffBezanson , sepertinya begini
operator dapat diimplementasikan jika ada cara untuk melakukan infix macro. Apakah kamu
tahu apakah ada masalah ideologis dengan itu, atau tidak diterapkan?

-
Balas email ini secara langsung atau lihat di Gi tHubhttps: //github.com/JuliaLang/julia/issues/5571#issuecomment -34374347
.

benar, saya pasti tidak ingin ini menjadi kasus khusus. Menanganinya dalam desain API Anda sebenarnya tidak terlalu buruk, dan bahkan batasan argumen variabel tidak terlalu menjadi masalah jika Anda memiliki jenis anotasi untuk disambiguasi.

function move(obj::MyType, x, y, args...)
    # do stuff
    obj
end

move(args...) = obj::MyType -> move(obj, args...)

Saya pikir perilaku ini dapat ditangani oleh makro @composable yang akan menangani deklarasi ke-2.

Ide makro infix menarik bagi saya dalam situasi di mana itu akan disatukan dengan mendeklarasikan fungsi infix, yang dibahas di # 4498.

Mengapa pencipta Julia sangat menentang mengizinkan objek berisi metode mereka sendiri? Di mana saya dapat membaca lebih lanjut tentang keputusan itu? Pikiran dan teori apa yang ada di balik keputusan itu?

@meglio tempat yang lebih berguna untuk pertanyaan umum adalah milis atau tag StackOverflow julia-lang . Lihat pembicaraan Stefan dan arsip daftar pengguna dan pengembang untuk diskusi sebelumnya tentang topik ini.

Hanya menimpali, bagi saya hal yang paling intuitif adalah memiliki beberapa placeholder diganti dengan
nilai ekspresi sebelumnya dalam urutan hal-hal yang Anda coba buat, mirip dengan makro as-> clojure. Jadi ini:

<strong i="8">@as</strong> _ begin
    3+3
    f(_,y)
    g(_) * h(_,z)
end

akan diperluas menjadi:

g(f(3+3,y)) * h(f(3+3,y),z)

Anda dapat memikirkan ekspresi pada baris sebelumnya "turun ke bawah" untuk mengisi lubang garis bawah pada baris berikutnya.

Saya mulai membuat sketsa sesuatu yang kecil seperti kuartal terakhir ini dalam pertarungan penundaan minggu terakhir.

Kami juga dapat mendukung versi oneliner menggunakan |> :

<strong i="19">@as</strong> _ 3+3 |> f(_,y) |> g(_) * h(_,z)

@porterjamesj , saya suka ide itu!

Saya setuju; itu cukup bagus, dan memiliki sifat umum yang menarik.
Pada 7 Feb 2014 15.19, "Kevin Squire" [email protected] menulis:

@porterjamesj https://github.com/porterjamesj , saya suka ide itu!

Balas email ini secara langsung atau lihat di Gi tHubhttps: //github.com/JuliaLang/julia/issues/5571#issuecomment -34497703
.

Saya menyukai ide @porterjamesj bukan hanya karena menghirup udara segar, tetapi karena tampaknya jauh lebih fleksibel daripada ide sebelumnya. Kami tidak menikah hanya dengan menggunakan argumen pertama, kami memiliki kebebasan bebas pada pilihan variabel perantara, dan ini juga sepertinya sesuatu yang dapat kami implementasikan sekarang tanpa harus menambahkan sintaks baru atau kasus-khusus ke bahasa.

Perhatikan bahwa di Julia, karena kita tidak melakukan banyak pola obj.method(args...) , dan sebaliknya melakukan pola method(obj, args...) , kita cenderung tidak memiliki metode yang mengembalikan objek tempat mereka beroperasi untuk ekspres. tujuan rangkaian metode. (Itulah yang dilakukan jQuery , dan fantastis dalam javascript). Jadi kami tidak menyimpan banyak pengetikan di sini, tetapi untuk tujuan memiliki "pipa" penyetelan antar fungsi, menurut saya ini sangat bagus.

Mengingat bahwa clojure -> dan ->> hanyalah kasus khusus di atas, dan cukup umum, kita mungkin bisa menerapkannya dengan cukup mudah juga. Meskipun pertanyaan tentang apa yang memanggil mereka agak rumit. Mungkin @threadfirst dan @threadlast ?

Saya suka gagasan ini menjadi makro juga.

Bukankah lebih baik jika ekspansi, mengikuti contoh, seperti itu

tmp = 3+3; tmp = f(tmp); return h(tmp, z)

untuk menghindari beberapa panggilan ke operasi yang sama? (Mungkin itu sudah tersirat dalam ide @porterjamesj )

Saran lain: apakah mungkin makro memperluas pintasan f menjadi f(_) dan f(y) menjadi f(_,y) ? Mungkin akan terlalu banyak, tetapi saya pikir kemudian kita memiliki opsi untuk menggunakan placeholder hanya jika diperlukan ... (pintasan harus, bagaimanapun, hanya diperbolehkan pada pemanggilan fungsi saja, bukan pada ekspresi seperti g(_) * h(_,z) atas)

@cdsousa Intinya tentang menghindari beberapa panggilan adalah bagus. Implementasi clojure menggunakan pengikatan let berurutan untuk mencapai ini; Saya tidak yakin apakah kita bisa lolos dari ini karena saya tidak cukup tahu tentang kinerja let .

Jadi, apakah makro @as menggunakan jeda baris dan => sebagai titik pemisah untuk memutuskan apa ekspresi substitusi dan apa yang diganti?

let kinerja bagus; sekarang dapat secepat penugasan variabel jika memungkinkan, dan juga cukup cepat sebaliknya.

@ssfrr dalam implementasi mainan saya hanya menyaring semua node terkait linebreak yang disisipkan parser (NB, saya tidak begitu mengerti semua ini, mungkin akan lebih baik untuk memiliki dokumentasi tentang mereka di manual) dan kemudian mengurangi substitusi atas daftar ekspresi yang tersisa. Menggunakan let akan lebih baik meskipun saya pikir.

@rumahsakitotak :

Saran lain: apakah mungkin makro memperluas pintasan f menjadi f(_) dan f(y) menjadi f(_,y)

f hingga f(_) masuk akal bagi saya. Untuk yang kedua, saya berpendapat bahwa menentukan lokasi secara eksplisit lebih baik, karena orang yang berakal sehat dapat berargumen bahwa f(_,y) atau f(y,_) lebih alami.

Mengingat bahwa clojure -> dan ->> hanyalah kasus khusus di atas, dan cukup umum, kita mungkin bisa menerapkannya dengan cukup mudah juga. Meskipun pertanyaan tentang apa yang memanggil mereka agak rumit. Mungkin @threadfirst dan @threadlast ?

Saya pikir menentukan lokasi yang jelas dengan f(_,y...) atau f(y..., _) memungkinkan kode menjadi cukup dimengerti. Meskipun sintaks tambahan (dan operator) masuk akal di Clojure, kami tidak benar-benar memiliki operator tambahan yang tersedia, dan menurut saya makro tambahan umumnya akan membuat kode menjadi kurang jelas.

Jadi, apakah makro @as menggunakan jeda baris dan => sebagai titik pemisah untuk memutuskan apa ekspresi substitusi dan apa yang diganti?

Saya akan berpikir lebih alami menggunakan |> sebagai titik terpisah, karena sudah digunakan untuk pipelining

Sekadar info , ada implementasi makro threading di

@>> range() map(x->x^2) filter(iseven)

Di sisi positifnya, ini tidak memerlukan perubahan bahasa apa pun, tetapi menjadi sedikit jelek jika Anda ingin menggunakan lebih dari satu baris.

Saya juga bisa menerapkan @as> di Lazy.jl jika ada minat. Lazy.jl sekarang memiliki makro @as juga.

Anda juga dapat melakukan sesuatu seperti ini (meskipun menggunakan sintaks mirip Haskell) dengan Monads.jl (catatan: perlu diperbarui untuk menggunakan sintaks Julia saat ini). Tetapi saya menduga bahwa versi khusus untuk penguliran argumen saja seharusnya dapat menghindari kesalahan kinerja yang dimiliki pendekatan umum.

Lazy.jl terlihat seperti paket yang sangat bagus, dan dipelihara secara aktif. Apakah ada alasan kuat mengapa hal ini perlu dilakukan di Base?

Bagaimana fungsi rangkaian berfungsi dengan fungsi yang mengembalikan banyak nilai?
Apa hasil dari rangkaian misalnya:

function foo(a,b)
    a+b, a*b   # x,y respectively
end

dan bar(x,z,y) = x * z - y menjadi?

Bukankah itu membutuhkan sintaks seperti bar(_1,z,_2) ?

Melempar contoh lain:

data = [2.255, 3.755, 6.888, 7.999, 9.001]

Cara bersih untuk menulis: log(sum(round(data))) adalah data|>round|>sum|>log
Tetapi jika kita ingin membuat log basis 2, dan ingin membulatkan menjadi 3 desimal,
maka: kita hanya dapat menggunakan formulir pertama:
log(2,sum(round(data,3)))

Tapi idealnya kami ingin bisa melakukan:
data|>round(_,3)|>sum|>log(2,_)
(atau serupa)

Saya telah membuat prototipe tentang bagaimana saya menyarankan itu harus bekerja.
https://github.com/oxinabox/Pipe.jl

Ini tidak menyelesaikan poin @gregid , tapi saya sedang mengerjakannya sekarang.
Ini juga tidak menangani kebutuhan untuk memperluas argumen

Ini mirip dengan makro threading Lazy.jl @ one-more-minute's tetapi menyimpan simbol |> agar mudah dibaca (preferensi pribadi).

Saya perlahan-lahan akan membuatnya menjadi satu paket, mungkin, di beberapa titik

Satu opsi lagi adalah:

data |>   x -> round(x,2)  |> sum |>  x -> log(2,x)

Meskipun lebih dari log(2,sum(round(data,2))) , notasi ini terkadang membantu keterbacaan.

@shashi itu tidak buruk, tidak memikirkan itu,
Saya pikir secara umum terlalu bertele-tele agar mudah dibaca

https://github.com/oxinabox/Pipe.jl Sekarang menyelesaikan masalah @gregid .
Meskipun jika Anda meminta _[1] dan _[2] hal ini dilakukan dengan melakukan beberapa panggilan ke subsitusi
Yang saya tidak yakin adalah perilaku yang paling diinginkan.

Sebagai orang luar, saya pikir operator jalur pipa akan mendapatkan keuntungan dari mengadaptasi perlakuan F # terhadapnya.
Memang, F # memiliki kari, tetapi beberapa keajaiban mungkin bisa dilakukan di bagian belakang agar tidak mengharuskannya. Seperti, dalam implementasi operator, dan bukan bahasa inti.

Ini akan menghasilkan [1:10] |> map(e -> e^2) menghasilkan [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] .

Melihat ke belakang, @ssfrr menyinggung hal ini, tetapi argumen obj dalam contoh mereka akan secara otomatis diberikan ke map sebagai argumen kedua dalam contoh saya, sehingga menghemat pemrogram dari keharusan untuk mendefinisikan fungsinya untuk dukung itu.

Apa yang Anda usulkan?

Pada 5 Juni 2015, pukul 17:22, H-225 [email protected] menulis:

Sebagai orang luar, saya pikir salah satu cara yang lebih baik untuk melakukan ini adalah dengan menyesuaikan perlakuan F # terhadapnya.
Memang, F # memiliki kari, tetapi beberapa keajaiban mungkin bisa dilakukan di bagian belakang agar tidak mengharuskannya. Seperti, dalam implementasi operator, dan bukan bahasa inti.

Ini akan membuat [1:10] |> map (e -> e ^ 2) menghasilkan [1, 4, 9, 16, 25, 36, 49, 64, 81, 100].

Secara pribadi, saya pikir itu bagus dan jelas tanpa terlalu bertele-tele.

Jelas, seseorang dapat menulis result = map (sqr, [1:10]), tetapi mengapa mereka harus menggunakan operator pipeline?
Mungkin ada sesuatu yang saya lewatkan?

-
Balas email ini secara langsung atau lihat di GitHub.

@Tokopedia
Pada dasarnya, minta operator bekerja seperti:

  • x |> y(f) = y(x, f)
  • x |> y(f) = y(f, x)

Mungkin memiliki pola antarmuka di mana fungsi apa pun yang akan digunakan dengan operator mengambil data untuk dioperasikan sebagai argumen pertama atau terakhir, bergantung pada yang mana di atas yang dipilih untuk menjadi pola itu.
Jadi, untuk fungsi map sebagai contoh, map bisa jadi map(func, data) atau map(data, func) .

Apakah itu lebih jelas?

Lazy.jl terlihat seperti paket yang sangat bagus, dan dipelihara secara aktif. Apakah ada alasan kuat mengapa hal ini perlu dilakukan di Base?

Saya pikir inilah pertanyaan penting di sini.

Alasan ini mungkin diinginkan di base adalah 2 kali lipat:

1.) Kami mungkin ingin mendorong pipelining sebagai Julian Way - argumen dapat dibuat agar lebih mudah dibaca
2.) hal-hal seperti Lazy.jl, FunctionalData.jl, dan Pipe.jl saya sendiri memerlukan makro untuk membungkus ekspresi yang akan ditindaklanjuti - yang membuatnya kurang dapat dibaca.

Saya merasa jawabannya mungkin terletak pada memiliki Infix Macros.
Dan mendefinisikan |> seperti itu.

Saya tidak yakin memiliki |>, (atau sepupu mereka blok do) yang termasuk dalam inti sama sekali.
Tetapi alat tidak ada untuk menentukannya di luar parser.

Kemampuan untuk memiliki sintaks pipelining semacam itu tampaknya sangat bagus. Mungkinkah itu ditambahkan ke Base, yaitu x |> y(f) = y(f, x) part, yang dapat digunakan oleh Lazy.j, FunctionalData.jl, dan Pipe.jl? : +1:

Setelah melihat kode yang menggunakan berbagai implementasi ini dalam paket, saya pribadi merasa itu tidak dapat dibaca dan sangat tidak Julian. Permainan pipeline kiri-ke-kanan tidak membantu keterbacaan, itu hanya membuat kode Anda menonjol sebagai kebalikan dari kode normal sempurna lainnya yang menggunakan tanda kurung untuk evaluasi fungsi. Saya lebih suka mencegah sintaks yang mengarah ke 2 gaya berbeda di mana kode yang ditulis dalam gaya mana pun terlihat di dalam dan ke belakang relatif terhadap kode yang ditulis di gaya lain. Mengapa tidak hanya menggunakan sintaks yang sangat baik yang sudah kita miliki dan mendorong agar segala sesuatunya terlihat lebih seragam?

@tel
Secara pribadi, saya melihatnya dari sudut pandang yang agak bermanfaat.
Memang, mungkin jika Anda melakukan sesuatu yang sederhana maka itu tidak perlu, tetapi jika Anda menulis fungsi katakan, itu melakukan sesuatu yang cukup rumit, atau bertele-tele, (di luar kepala saya: manipulasi data misalnya), maka saya pikir di situlah sintaks pipeline bersinar.

Saya mengerti apa yang Anda maksud; _would_ menjadi lebih seragam jika Anda memiliki satu sintaks pemanggilan fungsi untuk semuanya. Secara pribadi, menurut saya lebih baik membuatnya lebih mudah untuk menulis kode [rumit] yang dapat dengan mudah dipahami. Memang, Anda harus mempelajari sintaks dan artinya, tetapi, IMHO, |> tidak lebih sulit dipahami daripada cara memanggil fungsi.

@tkelman Saya akan melihatnya dari sudut pandang yang berbeda. Jelas, ada orang yang lebih menyukai gaya pemrograman itu. Saya dapat melihat bahwa mungkin Anda ingin memiliki gaya yang konsisten untuk kode sumber ke Basis, tetapi ini hanya tentang menambahkan dukungan parser untuk gaya pemograman _their_ aplikasi Julia yang mereka sukai. Apakah orang Julian benar-benar ingin mencoba mendikte atau melumpuhkan sesuatu yang menurut orang lain bermanfaat?
Saya telah menemukan hal-hal pipelining bersama-sama sangat berguna di Unix, jadi meskipun saya tidak pernah menggunakan bahasa pemrograman yang mengaktifkannya dalam bahasa tersebut, saya setidaknya memberikan manfaat dari keraguan.

Kami memiliki |> sebagai operator pemipaan fungsi, tetapi ada batasan implementasi tentang bagaimana hal itu dilakukan saat ini yang membuatnya sangat lambat saat ini.

Perpipaan sangat bagus dalam shell unix di mana semuanya mengambil teks masuk dan keluar teks. Dengan tipe yang lebih rumit dan banyak input dan output, ini tidak begitu jelas. Jadi kami memiliki dua sintaks, tetapi satu lebih kurang masuk akal dalam kasus MIMO. Dukungan parser untuk gaya pemrograman alternatif atau DSL biasanya tidak diperlukan karena kami memiliki makro yang kuat.

Oke, terima kasih, saya mengikuti komentar @oxinabox :

Tetapi alat tidak ada untuk menentukannya di luar parser.

Apakah dipahami apa yang akan dilakukan untuk menghapus batasan penerapan yang Anda rujuk?

Beberapa saran sebelumnya berpotensi diimplementasikan dengan membuat |> parse argumennya sebagai makro alih-alih sebagai fungsi. Arti pemipaan objek perintah sebelumnya dari |> telah ditinggalkan, jadi ini mungkin benar-benar dibebaskan untuk melakukan sesuatu yang berbeda dengan, come 0.5-dev.

Namun pilihan ini sedikit mengingatkan saya pada parsing khusus ~ yang menurut saya merupakan kesalahan karena alasan yang telah saya sebutkan di tempat lain.

Parsing ~ tidak masuk akal, ini adalah fungsi dalam basis. Menggunakan _ , _1 , _2 , tampaknya _more_ masuk akal (terutama jika Anda menaikkan jika variabel-variabel ini didefinisikan di tempat lain dalam lingkup). Masih sampai kita memiliki fungsi anonim yang lebih efisien, ini sepertinya tidak akan berfungsi ...

diimplementasikan dengan membuat |> mengurai argumennya sebagai makro alih-alih sebagai fungsi

Kecuali Anda melakukan itu!

Parsing ~ tidak masuk akal, ini adalah fungsi dalam basis

Ini adalah operator unary untuk versi bitwise. Infix binary ~ mem-parsing sebagai makro, ref https://github.com/JuliaLang/julia/issues/4882 , yang menurut saya adalah penggunaan yang aneh dari operator ascii (https://github.com/ JuliaLang / julia / pull / 11102 # Issuecomment-98477891).

@tel

Jadi kami memiliki dua sintaks, tetapi satu lebih kurang masuk akal dalam kasus MIMO.

3 Sintaks. Agak.
Pipa masuk, panggilan fungsi normal dan blok-Do.
Bahkan bisa diperdebatkan 4, karena Macro menggunakan konvensi yang berbeda juga.


Untuk saya,
Urutan Pembacaan (yaitu dari kiri ke kanan) == Urutan aplikasi, membuat, untuk rantai fungsi SISO, jauh lebih jelas.

Saya melakukan banyak kode seperti (Menggunakan iterators.jl, dan pipe.jl):

  • loaddata(filename) |> filter(s-> 2<=length(s)<=15, _) |> take!(150,_) |> map(eval_embedding, _)
  • results |> get_error_rate(desired_results, _) |> round(_,2)

Untuk SISO lebih baik (untuk preferensi pribadi saya), untuk MIMO tidak.

Julia tampaknya sudah yakin bahwa ada beberapa cara yang benar untuk melakukan sesuatu.
Yang saya tidak yakin 100% adalah hal yang baik.

Seperti yang saya katakan, saya akan menyukai blok Pipe dan Do dipindahkan dari bahasa utama.

Do-block memiliki beberapa kasus penggunaan yang sangat membantu, tetapi itu sedikit mengganggu saya karena mereka harus menggunakan input pertama sebagai fungsinya, tidak selalu sesuai dengan filosofi pengiriman ganda (dan begitu pula pandas / UFCS gaya D dengan postfix data.map(f).sum() , saya tahu ini populer tetapi saya rasa ini tidak dapat digabungkan secara efektif dengan beberapa pengiriman).

Perpipaan mungkin bisa segera dihentikan, dan diserahkan ke paket untuk digunakan di DSL seperti Pipe.jl.

Julia tampaknya sudah yakin bahwa ada beberapa cara yang benar untuk melakukan sesuatu.
Yang saya tidak yakin 100% adalah hal yang baik.

Ini terkait dengan pertanyaan apakah kita dapat menegakkan panduan gaya komunitas secara ketat atau tidak. Sejauh ini kami belum melakukan banyak hal di sini, tetapi untuk interoperabilitas paket jangka panjang, konsistensi, dan keterbacaan, saya pikir ini akan menjadi semakin penting seiring dengan pertumbuhan komunitas. Jika Anda satu-satunya orang yang pernah membaca kode Anda, lakukanlah apa pun yang Anda inginkan. Jika tidak, ada nilai dalam menukar sedikit lebih buruk (menurut pendapat Anda) keterbacaan demi keseragaman.

@oxinabox @tkelman
Saya belum menemukan alasan yang jelas mengapa itu tidak boleh disertakan dalam bahasa, atau memang di paket "inti". [mis .: Base]
Secara pribadi, saya pikir membuat makro |> mungkin jawabannya.
Sesuatu yang _seperti_ ini mungkin? (Saya bukan programmer master Julia!)

macro (|>) (x, y::Union(Symbol, Expr))
    if isa(y, Symbol)
        y = Expr(:call, y) # assumes y is callable
    end
    push!(y.args, x)
    return eval(y)
end

Di bawah Julia v0.3.9, saya tidak dapat mendefinisikannya dua kali - sekali dengan simbol, dan sekali dengan ekspresi; pemahaman [terbatas] saya tentang Union adalah bahwa ada kinerja hit dari menggunakannya, jadi saya menduga itu akan menjadi sesuatu untuk diperbaiki dalam kode contoh mainan saya.

Tentu saja, ada masalah dengan sintaks penggunaan untuk ini.
Misalnya, untuk menjalankan setara dengan log(2, 10) , Anda harus menulis @|> 10 log(2) , yang tidak diinginkan di sini.
Pemahaman saya adalah bahwa Anda harus dapat entah bagaimana menandai fungsi / makro sebagai "infixable", seolah-olah, sehingga Anda dapat menuliskannya sebagai berikut: 10 |> log(2) . (Benar jika salah!)
Contoh yang dibuat-buat, saya tahu. Saya tidak bisa memikirkan yang bagus sekarang! =)

Penting juga untuk menunjukkan satu area yang belum saya bahas dalam contoh saya ...
Jadi misal:

julia> for e in ([1:10], [11:20] |> zip) println(e) end
(1,11)
(2,12)
(3,13)
(4,14)
(5,15)
(6,16)
(7,17)
(8,18)
(9,19)
(10,20)

Sekali lagi - contoh yang dibuat-buat, tetapi semoga Anda mengerti maksudnya!
Saya melakukan beberapa mengutak-atik, tetapi saat menulis ini saya tidak dapat memahami bagaimana menerapkannya, saya sendiri.

Silakan lihat https://github.com/JuliaLang/julia/issues/554#issuecomment -110091527 dan # 11608.

Pada 9 Juni 2015, pukul 21:37, H-225 [email protected] menulis:

Saya belum menemukan alasan yang jelas mengapa itu tidak dimasukkan ke dalam bahasa

Ini adalah sikap mental yang salah untuk desain bahasa pemrograman. Pertanyaannya harus dengan "mengapa?" daripada "mengapa tidak?" Setiap fitur membutuhkan alasan yang kuat untuk penyertaannya, dan bahkan dengan alasan yang bagus, Anda harus berpikir panjang dan keras sebelum menambahkan apapun. Bisakah kamu hidup tanpanya? Apakah ada cara lain untuk mencapai hal yang sama? Adakah variasi fitur yang berbeda yang lebih baik dan lebih umum atau lebih ortogonal dengan fitur yang ada? Saya tidak mengatakan bahwa ide khusus ini tidak dapat terjadi, tetapi perlu ada pembenaran yang jauh lebih baik daripada "mengapa tidak?" dengan beberapa contoh yang tidak lebih baik dari sintaks normal.

Pertanyaannya harus dengan "mengapa?" daripada "mengapa tidak?"

+ 1_000_000

Memang.
Lihat postingan blog yang cukup terkenal ini:
Setiap fitur dimulai dengan -100 poin.
Perlu dilakukan peningkatan besar agar layak ditambahkan ke bahasa.

FWIW, Pyret (http://www.pyret.org/) melalui diskusi yang tepat ini beberapa bulan yang lalu. Bahasa ini mendukung notasi "bola meriam" yang awalnya berfungsi seperti yang diajukan orang dengan |> . Di Pyret,

[list: 1, 2, 3, 5] ^ map(add-one) ^ filter(is-prime) ^ sum() ^ ...

Jadi, notasi peluru meriam dirancang untuk menambahkan argumen ke fungsi.

Tidak butuh waktu lama sebelum mereka memutuskan bahwa sintaks ini terlalu membingungkan. Mengapa sum() dipanggil tanpa argumen? dll. Akhirnya, mereka memilih alternatif kari yang elegan:

[list: 1, 2, 3, 5] ^ map(_, add-one) ^ filter(_, is-prime) ^ sum() ^ ...

Ini memiliki keuntungan karena lebih eksplisit dan menyederhanakan operator ^ menjadi fungsi sederhana.

Ya, itu tampaknya jauh lebih masuk akal bagi saya. Ini juga lebih fleksibel daripada kari.

@StefanKarpinski Saya agak bingung. Apakah Anda bermaksud mengatakan lebih fleksibel daripada merantai (bukan kari)? Bagaimanapun, solusi Pyret adalah dengan hanya menggunakan kari, yang lebih umum daripada rantai.

Mungkin, jika kita memodifikasi sintaks |> sedikit (saya benar-benar tidak tahu seberapa sulit menerapkannya, mungkin bertentangan dengan | dan > ), kita dapat mengatur sesuatu yang fleksibel dan mudah dibaca.

Mendefinisikan sesuatu seperti

foo(x,y) = (y,x)
bar(x,y) = x*y

Kami akan memiliki:

randint(10) |_> log(_,2) |> sum 
(1,2) |_,x>  foo(_,x)   |x,_>   bar(_,2) |_> round(_, 2) |> sum |_> log(_, 2)

Dengan kata lain, kita akan memiliki operator seperti |a,b,c,d> dimana a , b , c dan d akan mendapatkan nilai yang dikembalikan dari ekspresi terakhir (secara berurutan) dan gunakan dalam placeholder di dalam ekspresi berikutnya.

Jika tidak ada variabel di dalam |> itu akan bekerja seperti sekarang. Kita juga bisa menetapkan standar baru: f(x) |> g(_, 1) akan mendapatkan semua nilai yang dikembalikan oleh f(x) dan dikaitkan dengan placeholder _ .

@samuela , yang saya maksud adalah bahwa dengan kari Anda hanya dapat menghilangkan argumen yang tertinggal, sedangkan dengan pendekatan _ , Anda dapat menghilangkan argumen apa pun dan mendapatkan fungsi anonim. Yaitu diberi f(x,y) dengan kari Anda dapat melakukan f(x) untuk mendapatkan fungsi yang menghasilkan y -> f(x,y) , tetapi dengan garis bawah Anda dapat melakukan f(x,_) untuk hal yang sama tetapi juga lakukan f(_,y) untuk mendapatkan x -> f(x,y) .

Meskipun saya menyukai sintaks garis bawah, saya masih belum puas dengan jawaban yang diajukan untuk pertanyaan tentang seberapa banyak ekspresi di sekitarnya yang "ditangkap".

apa yang Anda lakukan jika suatu fungsi mengembalikan banyak hasil? Apakah itu harus memberikan tupel ke posisi _? Atau mungkinkah ada sintaks untuk membaginya dengan cepat? Mungkin pertanyaan bodoh, jika demikian, maaf!

@StefanKarpinski Ah, saya mengerti maksud Anda. Sepakat.

@ScottPJones , jawaban yang jelas adalah mengizinkan panah seni ASCII:
http://scrambledeggsontoast.github.io/2014/09/28/needle-announce/

@simonbyrne Itu terlihat lebih buruk daripada pemrograman di Fortran IV pada kartu berlubang, seperti yang saya lakukan di masa muda saya yang salah bicara! Hanya bertanya-tanya apakah beberapa sintaks seperti _1, _2, dll. Mungkin memungkinkan memisahkan beberapa pengembalian, atau apakah itu hanya ide bodoh di pihak saya?

@nimonby. Itu brilian. Menerapkannya sebagai makro string akan menjadi proyek GSoC yang luar biasa.

Mengapa sum () dipanggil tanpa argumen apa pun?

Saya pikir argumen implisit juga merupakan salah satu hal yang lebih membingungkan tentang notasi do , jadi alangkah baiknya jika kita dapat menggunakan konvensi yang sama untuk itu juga (meskipun saya menyadari bahwa ini jauh lebih sulit, karena sudah dimasukkan ke dalam bahasa).

@simonbyrne Anda tidak berpikir itu bisa dilakukan dengan cara yang tidak ambigu? Jika demikian, itu adalah sesuatu yang menurut saya layak untuk dilanggar (notasi do ), jika dapat dibuat lebih logis, lebih umum, dan konsisten dengan rangkaian.

@simonbyrne Ya, saya setuju. Saya memahami motivasi untuk notasi do tetapi saya merasa sangat yakin bahwa itu tidak membenarkan senam sintaksis.

@samuela tentang peta (f, _) vs hanya peta (f). Saya setuju bahwa beberapa desugaring sihir akan membingungkan, tetapi menurut saya map (f) adalah sesuatu yang seharusnya ada. Tidak perlu dan gula cukup menambahkan metode sederhana untuk memetakan.
misalnya

map(f::Base.Callable) = function(x::Any...) map(f,x...) end

ie map mengambil fungsi dan kemudian mengembalikan fungsi yang bekerja pada hal-hal yang dapat diulang (lebih atau kurang).

Lebih umum saya pikir kita harus bersandar pada fungsi yang memiliki metode "kenyamanan" tambahan, daripada semacam konvensi bahwa |> selalu memetakan data ke argumen pertama (atau serupa).

Dalam nada yang sama mungkin ada a

type Underscore end
_ = Underscore()

dan konvensi umum bahwa fungsi harus / bisa memiliki metode yang mengambil garis bawah dalam argumen tertentu, lalu mengembalikan fungsi yang mengambil lebih sedikit argumen. Saya kurang yakin bahwa ini adalah ide yang bagus, karena seseorang perlu menambahkan metode 2 ^ n untuk setiap fungsi yang membutuhkan n argumen. Tapi itu satu pendekatan. Saya bertanya-tanya apakah mungkin untuk tidak harus secara eksplisit menambahkan begitu banyak metode melainkan menghubungkan ke metode pencarian, sehingga jika ada argumen jenis Underscore maka fungsi yang sesuai dikembalikan.

Bagaimanapun, saya pasti berpikir memiliki versi peta dan filter yang hanya mengambil yang dapat dipanggil dan mengembalikan yang dapat dipanggil masuk akal, hal dengan Garis Bawah mungkin atau mungkin tidak bisa diterapkan.

@tokopedia
Saya membayangkan bahwa x |> map(f, _) => x |> map(f, Underscore()) => x |> map(f, x) , seperti yang Anda usulkan, akan menjadi cara termudah untuk mengimplementasikan map(f, _) , bukan? - hanya memiliki _ menjadi entitas khusus yang Anda programkan?
</s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> orang </s>
Meskipun, saya tidak yakin apakah itu lebih baik daripada membuatnya disimpulkan secara otomatis oleh Julia-- mungkin menggunakan sintaks |> daripada harus memprogramnya sendiri.

Juga, mengenai proposal Anda untuk map - Saya menyukainya. Memang, untuk |> itu akan sangat berguna. Padahal, saya membayangkan akan lebih sederhana lebih baik untuk hanya menerapkan kesimpulan otomatis x |> map(f, _) => x |> map(f, x) sebagai gantinya?

@StefanKarpinski Masuk akal. Tidak pernah memikirkannya seperti itu.

Tidak ada yang saya katakan akan terikat dengan |> dengan cara apa pun. Apa yang saya maksud tentang _ akan menjadi contoh untuk menambahkan metode ke < seperti itu:

<(_::Underscore, x) = function(z) z < x end
<(x, _::Underscore) = function(z) x < z end

Tetapi sekali lagi saya pikir ini akan merepotkan kecuali ada cara untuk menambahkan metode yang sesuai secara otomatis.

Sekali lagi, hal dengan garis bawah terpisah yang menambahkan metode kemudahan untuk memetakan seperti yang diuraikan di atas. Saya pikir keduanya harus ada, dalam beberapa bentuk atau lainnya.

@patrickthebold Pendekatan seperti itu dengan tipe yang ditentukan pengguna untuk garis bawah, dll akan memberikan beban yang signifikan dan tidak perlu pada programmer saat mengimplementasikan fungsi. Harus mencantumkan semua 2 ^ n dari

f(_, x, y) = ...
f(x, _, y) = ...
f(_, _, y) = ...
...

akan sangat menyebalkan, apalagi janggal.

Juga, proposisi Anda dengan map akankah saya anggap menyediakan sintaks solusi untuk map(f) dengan fungsi dasar seperti map dan filter tetapi secara umum menderita hal yang sama kompleksitas masalah sebagai pendekatan garis bawah manual. Misalnya, untuk func_that_has_a_lot_of_args(a, b, c, d, e) Anda harus melalui proses yang melelahkan dengan mengetikkan setiap kemungkinan "kari"

func_that_has_a_lot_of_args(a, b, c, d, e) = ...
func_that_has_a_lot_of_args(b, c, d, e) = ...
func_that_has_a_lot_of_args(a, b, e) = ...
func_that_has_a_lot_of_args(b, d, e) = ...
func_that_has_a_lot_of_args(a, d) = ...
...

Dan bahkan jika Anda melakukannya, Anda masih akan dihadapkan dengan jumlah ambiguitas yang tidak masuk akal saat memanggil fungsi: Apakah func_that_has_a_lot_of_args(x, y, z) mengacu pada definisi di mana x=a,y=b,z=c atau x=b,y=d,z=e , dll. ? Julia akan membedakannya dengan informasi jenis runtime tetapi bagi programmer awam yang membaca kode sumber itu sama sekali tidak jelas.

Saya pikir cara terbaik untuk menyelesaikan kari garis bawah dengan benar adalah dengan memasukkannya ke dalam bahasa. Ini akan menjadi perubahan yang sangat mudah pada kompilator. Kapan pun garis bawah muncul di aplikasi fungsi, tarik saja untuk membuat lambda. Saya mulai mempertimbangkan untuk menerapkan ini beberapa minggu yang lalu tetapi sayangnya saya tidak berpikir saya akan memiliki cukup waktu luang dalam beberapa minggu ke depan untuk menyelesaikannya. Untuk seseorang yang akrab dengan kompiler Julia meskipun mungkin tidak lebih dari satu sore untuk menyelesaikan semuanya.

@samuela
Dapatkah Anda menjelaskan apa yang Anda maksud dengan, "tarik untuk membuat lambda"? - Saya penasaran. Saya juga bertanya-tanya bagaimana hal itu dapat diterapkan.

@tokopedia
Ah - begitu. Agaknya Anda kemudian dapat menggunakan hal seperti ini: filter(_ < 5, [1:10]) => [1:4] ?
Secara pribadi, saya akan menemukan filter(e -> e < 5, [1:10]) lebih mudah dibaca; lebih konsisten - makna yang tidak terlalu tersembunyi, meskipun saya akui, ini lebih ringkas.

Kecuali jika Anda memiliki contoh di mana itu benar-benar bersinar?

@samuela

Juga, proposisi Anda dengan peta akan saya kira memberikan sintaks solusi untuk map (f) dengan fungsi dasar seperti peta dan filter tetapi secara umum menderita masalah kompleksitas yang sama seperti pendekatan garis bawah manual.

Saya tidak menyarankan hal ini dilakukan secara umum, hanya untuk map dan filter , dan mungkin beberapa tempat lain yang terlihat jelas. Bagi saya, begitulah cara kerja map : ambil fungsi dan kembalikan fungsi. (cukup yakin itulah yang dilakukan Haskell.)

akan sangat menyebalkan, apalagi janggal.

Saya pikir kami sepakat tentang itu. Saya berharap akan ada cara untuk menambahkan sesuatu ke bahasa untuk menangani pemanggilan metode di mana beberapa argumen berjenis Underscore. Setelah dipikirkan lebih lanjut, saya pikir intinya adalah memiliki karakter khusus yang secara otomatis berkembang menjadi lambda, atau memiliki tipe khusus yang secara otomatis berkembang menjadi lambda. Aku juga tidak merasa kuat. Saya bisa melihat plus dan minus dari kedua pendekatan tersebut.

@ H-225 ya, yang digarisbawahi hanyalah kenyamanan sintaksis. Tidak yakin seberapa umum itu, tetapi Scala pasti memilikinya. Secara pribadi saya menyukainya, tapi saya pikir itu hanya salah satu gaya itu.

@ H-225 Nah, dalam hal ini saya pikir contoh yang menarik dan relevan adalah fungsi rantai. Dari pada harus menulis

[1, 2, 3, 5]
  |> x -> map(addone, x)
  |> x -> filter(isprime, x)
  |> sum
  |> x -> 3 * x
  |> ...

seseorang dapat dengan mudah menulis

[1, 2, 3, 5]
  |> map(addone, _)
  |> filter(isprime, _)
  |> sum
  |> 3 * _
  |> ...

Saya menemukan diri saya tanpa sadar menggunakan sintaks garis bawah ini (atau beberapa varian kecil) terus-menerus dalam bahasa yang mendukungnya dan hanya menyadari betapa bermanfaatnya itu ketika beralih ke bahasa yang tidak mendukungnya.

Sejauh yang saya tahu, saat ini setidaknya ada 3,5 perpustakaan / pendekatan yang mencoba untuk mengatasi masalah ini di Julia: fungsi Julia |> , Pipe.jl, Lazy.jl, dan 0,5 untuk Julia builtin do notasi yang serupa dalam semangat. Bukan untuk menjelekkan salah satu pustaka atau pendekatan ini, tetapi banyak di antaranya dapat sangat disederhanakan jika kari garis bawah didukung oleh Julia.

@samuela jika Anda ingin bermain-main dengan implementasi ide ini, Anda dapat mencoba FunctionalData.jl, di mana contoh Anda akan terlihat seperti ini:

<strong i="7">@p</strong> map [1,2,3,4] addone | filter isprime | sum | times 3 _

Bagian terakhir menunjukkan bagaimana menyalurkan input ke parameter kedua (default adalah argumen satu, dalam hal ini _ dapat dihilangkan). Umpan balik sangat dihargai!


Edit: di atas hanya ditulis ulang menjadi:

times(3, sum(filter(map([1,2,3,4],addone), isprime)))

yang menggunakan FunctionalData.map dan filter, bukan Base.map dan filter. Perbedaan utama adalah urutan argumen, perbedaan kedua adalah konvensi pengindeksan (lihat dokumen). Bagaimanapun, Base.map dapat dengan mudah digunakan dengan membalik urutan argumen. @p adalah aturan penulisan ulang yang cukup sederhana (kiri ke kanan menjadi inner-to-outer, ditambah dukungan untuk kari sederhana: <strong i="17">@p</strong> map data add 10 | showall menjadi

showall(map(data, x->add(x,10)))

Peretasan dapat memperkenalkan sesuatu seperti ini: https://github.com/facebook/hhvm/issues/6455. Mereka menggunakan $$ yang tidak termasuk dalam tabel untuk Julia ( $ sudah terlalu kelebihan beban).

FWIW, saya sangat suka solusi Hack untuk ini.

Saya juga menyukainya, reservasi utama saya adalah bahwa saya masih menyukai notasi lambda terser yang mungkin menggunakan _ untuk variabel / slot dan akan lebih baik untuk memastikan bahwa ini tidak bertentangan.

Tidak bisakah seseorang menggunakan __ ? Apa sintaks lambda yang Anda pikirkan? _ -> sqrt(_) ?

Tentu, kami bisa. Sintaks itu sudah berfungsi, ini lebih tentang sintaks yang tidak memerlukan panah, sehingga Anda dapat menulis sesuatu di sepanjang baris map(_ + 2, v) , masalah sebenarnya adalah seberapa banyak ekspresi di sekitarnya _ milik.

Bukankah Mathematica memiliki sistem serupa untuk argumen anonim? Bagaimana
mereka menangani lingkup batasan argumen tersebut?
Pada Selasa, 3 Nov 2015 jam 09.09 Stefan Karpinski [email protected]
menulis:

Tentu, kami bisa. Sintaks itu sudah berfungsi, ini lebih tentang sintaks
tidak memerlukan panah, sehingga Anda dapat menulis sesuatu di sepanjang baris
peta (_ + 2, v), masalah sebenarnya adalah seberapa banyak dari sekitarnya
ekspresi _ milik.

-
Balas email ini secara langsung atau lihat di GitHub
https://github.com/JuliaLang/julia/issues/5571#issuecomment -153383422.

https://reference.wolfram.com/language/tutorial/PureFunctions.html , menampilkan
simbol #, adalah apa yang saya pikirkan.
Pada Sel, 3 Nov 2015 jam 9:34 Jonathan Malmaud [email protected] menulis:

Bukankah Mathematica memiliki sistem serupa untuk argumen anonim? Bagaimana
mereka menangani lingkup batasan argumen tersebut?
Pada Selasa, 3 Nov 2015 jam 09.09 Stefan Karpinski [email protected]
menulis:

Tentu, kami bisa. Sintaks itu sudah berfungsi, ini lebih tentang sintaks
tidak memerlukan panah, sehingga Anda dapat menulis sesuatu di sepanjang baris
peta (_ + 2, v), masalah sebenarnya adalah seberapa banyak dari sekitarnya
ekspresi _ milik.

-
Balas email ini secara langsung atau lihat di GitHub
https://github.com/JuliaLang/julia/issues/5571#issuecomment -153383422.

Mathematica menggunakan & untuk membatasinya.

Daripada melakukan sesuatu yang umum seperti sintaks lambda yang lebih pendek (yang dapat mengambil ekspresi arbitrer dan mengembalikan fungsi anonim), kita bisa mengatasi masalah pembatas dengan membatasi ekspresi yang dapat diterima ke pemanggilan fungsi, dan variabel / slot yang dapat diterima ke seluruh parameter. Ini akan memberi kita sintaks kari multi-parameter yang sangat bersih à la Open Dyln . Karena _ menggantikan seluruh parameter, sintaksisnya bisa minimal, intuitif, dan tidak ambigu. map(_ + 2, _) akan diterjemahkan ke x -> map(y -> y + 2, x) . Kebanyakan ekspresi panggilan non-fungsi yang Anda ingin lambdafy mungkin akan lebih panjang dan lebih ramah ke -> atau do . Saya pikir trade-off dari kegunaan vs umum akan sepadan.

@durcan , kedengarannya menjanjikan - dapatkah Anda menjelaskan sedikit tentang aturan tersebut? Mengapa _ tetap berada di dalam argumen map sedangkan yang kedua menghabiskan seluruh ekspresi map ? Saya tidak jelas tentang apa artinya "membatasi ekspresi yang dapat diterima ke pemanggilan fungsi", atau apa arti "membatasi variabel / slot yang dapat diterima ke seluruh parameter" ...

Oke, saya rasa saya mengerti aturannya, setelah membaca beberapa dokumentasi Dylan itu, tetapi saya harus bertanya-tanya tentang memiliki map(_ + 2, v) berfungsi tetapi map(2*_ + 2, v) tidak berfungsi.

Ada juga bisnis yang sangat nitpicky yang artinya _ + 2 + _ berarti (x,y) -> x + 2 + y sedangkan _ ⊕ 2 ⊕ _ berarti y -> (x -> x + 2) + y karena + dan * adalah satu-satunya operator yang saat ini mengurai sebagai panggilan fungsi multi-argumen alih-alih sebagai operasi asosiatif berpasangan. Sekarang, dapat dikatakan bahwa ketidakkonsistenan ini harus diperbaiki, meskipun itu tampaknya mengharuskan _parser_ memiliki pendapat tentang operator mana yang asosiatif dan mana yang tidak, mana yang tampaknya buruk. Tetapi saya berpendapat bahwa skema apa pun yang memerlukan mengetahui apakah parser mem-parsing a + b + c sebagai panggilan fungsi tunggal atau panggilan bersarang mungkin agak dipertanyakan. Mungkinkah notasi infiks harus ditangani secara khusus? Tapi tidak, itu juga terasa mencurigakan.

Ya, Anda telah mencapai trade-off. Di satu sisi, string operator yang panjang adalah sintaks yang paling bermasalah mengingat beberapa pilihan parsing kami saat ini (meskipun sulit untuk menyalahkan semantik fitur bahasa karena bergantung pada semantik bahasa saat ini). Di sisi lain, string pemanggilan fungsi yang panjang adalah keunggulannya. Misalnya, kami dapat menulis ulang masalah Anda sebagai:

2*_ |> 2+_ |> map(_, v)

Bagaimanapun, saya tidak berpikir bahwa masalah infix harus menghalangi memiliki opsi bebas pembatas yang bersih. Ini akan sangat membantu dengan sebagian besar pemanggilan fungsi normal, yang merupakan jenis masalah yang dihadapi. Jika Anda mau, Anda dapat memiliki pembatas opsional untuk membantu menyelesaikan ambiguitas khusus ini (di sini saya mencuri & untuk peran itu):

_ ⊕ 2 ⊕ _    # y -> (x -> x + 2) + y
_ ⊕ 2 ⊕ _ &  # (y , x) -> x + 2 + y

Ini adalah proposal terbaik sejauh ini, tetapi saya tidak sepenuhnya terjual. Cukup jelas apa yang terjadi saat pemanggilan fungsi secara eksplisit, tetapi kurang jelas saat pemanggilan fungsi diimplikasikan oleh sintaks infix.

Saya suka menganggap pendekatan ini lebih sebagai kari yang fleksibel dan umum, daripada lambda yang pendek dan manis (dan bahkan kemudian kita bisa mendapatkan hampir semua cara di sana dengan pembatas opsional). Saya akan menyukai sesuatu yang lebih sempurna, tetapi tanpa menambahkan lebih banyak suara simbolis (antitesis dari masalah ini) saya tidak yakin bagaimana menuju ke sana.

Ya, saya suka kecuali untuk infix. Bagian itu mungkin bisa diperbaiki.

Nah, kari dalam posisi infix bisa jadi merupakan kesalahan sintaks:

map(+(*(2, _), 2), v)      # curry is OK syntax, but obviously not what you wanted
map(2*_ + 2, v)            # ERROR: syntax: infix curry requires delimitation
map(2*_ + 2 &, v)          # this means what we want
map(*(2,_) |> +(_,2), v)   # as would this

Kurasa ini juga bisa menjadi peringatan.

Menyebut kari ini, menurut saya membingungkan dan salah.

Tentu, ini lebih seperti aplikasi fungsi parsial (yang secara opsional menjadi lambda yang berargumen secara anonim) kurasa. Mengesampingkan nama, ada pikiran?

Saya sedang memikirkan sesuatu seperti ini:

  • Jika _ muncul sendiri sebagai salah satu argumen dari ekspresi pemanggilan fungsi, pemanggilan fungsi itu diganti dengan ekspresi fungsi anonim yang mengambil argumen sebanyak fungsi memiliki argumen _ , yang tubuhnya adalah ekspresi asli dengan argumen _ diganti dengan argumen lambda secara berurutan.
  • Jika _ muncul di tempat lain, ekspresi sekitarnya hingga tetapi tidak termasuk tingkat prioritas panah atau tanda kurung di sekitarnya (tetapi bukan tanda kurung siku atau tanda kurung kurawal), diganti dengan fungsi anonim yang mengambil argumen sebanyak _ dalam ekspresi ini, yang tubuhnya adalah ekspresi asli dengan _ instance diganti dengan argumen lambda secara berurutan.

Contoh:

  • f(_, b)x -> f(x, b)
  • f(a, _)x -> f(a, x)
  • f(_, _)(x, y) -> f(x, y)
  • 2_^2x -> 2x^2
  • 2_^_(x, y) -> 2x^y
  • map(_ + 2, v)map(x -> x + 2, v)
  • map(2_ + 2, v)map(x -> 2x + 2, v)
  • map(abs, _)x -> map(abs, x)
  • map(2_ + 2, _)x -> map(y -> 2y + 2, x)
  • map(2_ - _, v, w)map((x, y) -> 2x - y, v, w)
  • map(2_ - _, v, _)x -> map((y, z) -> 2y - z, v, x)
  • map(2_ - _, _, _)(x, y) -> map((z, w) -> 2z - w, x, y)
  • _x -> x
  • map(_, v)x -> map(x, v)
  • map((_), v)map(x -> x, v)
  • f = _f = x -> x
  • f = 2_f = x -> 2x
  • x -> x^_x -> y -> x^y
  • _ && _(x, y) -> x && y
  • !_ && _(x, y) -> !x && y

Satu-satunya tempat ini mulai tidak pasti adalah kondisional - contoh-contoh itu menjadi agak aneh.

Ini masih sedikit rumit dan tidak berprinsip dan ada kasus sudut, tapi saya berhasil.

Ini menurut saya ide yang sangat buruk, hampir semua sintaks di Julia cukup familiar jika Anda telah menggunakan bahasa pemrograman lain. Orang yang melihat gula sintaks seperti ini tidak akan tahu apa yang akan "menguntungkan" untuk menyimpan beberapa karakter.

Contoh yang membuat saya sedikit kurang senang:

  • 2v[_]x -> 2v[x] (baik)
  • 2f(_)2*(x -> f(x)) (tidak terlalu bagus)
  • _ ? "true" : "false"(x -> x) ? "true" : "false"
  • _ ? _ : 0(x -> x) ? (y -> y) : 0

Saya pikir sesuatu yang lebih dalam sedang terjadi di sini - ada beberapa gagasan tentang posisi sintaksis di mana objek fungsi _make sense_ - dan Anda ingin memperluas ke yang terdekat dari itu. Posisi klasik yang "menginginkan" objek fungsi adalah argumen untuk fungsi lain, tetapi sisi kanan suatu tugas adalah tempat lain yang dapat dikatakan "menginginkan" objek fungsi (atau mungkin lebih akurat untuk mengatakan bahwa lebih suka mengambil fungsi menjadi tubuh suatu fungsi).

Mungkin, tetapi argumen yang sama dapat dibuat (dan telah) dibuat tentang sintaks do-block, yang menurut saya, secara keseluruhan, telah sangat berhasil dan berguna. Ini terkait erat dengan sintaks yang lebih baik untuk vektorisasi. Ini juga belum pernah terjadi sebelumnya - Scala menggunakan _ dengan cara yang sama, dan Mathematica menggunakan # cara yang sama.

Saya pikir Anda juga bisa membuat kasus yang _unfamiliar_ pilihan
pada dasarnya beberapa pengiriman alih-alih operator titik pengiriman tunggal
memaksa keputusan untuk memiliki sintaks yang ringkas untuk argumen pronominal
memulihkan urutan SVO yang sudah dikenal orang.

Pada Selasa, 17 November 2015 pukul 12.09, Stefan Karpinski [email protected]
menulis:

Mungkin, tetapi argumen yang sama bisa (dan telah) dibuat tentang do-block
sintaks, yang menurut saya, secara keseluruhan, sangat berhasil dan bermanfaat.
Ini terkait erat dengan sintaks yang lebih baik untuk vektorisasi. Ini juga tidak
yang belum pernah terjadi sebelumnya - Scala menggunakan _ dengan cara yang sama, dan Mathematica menggunakan #
demikian pula.

-
Balas email ini secara langsung atau lihat di GitHub
https://github.com/JuliaLang/julia/issues/5571#issuecomment -157437223.

Ini juga ada di C ++ dengan beberapa solusi perpustakaan di Boost khususnya, yang menggunakan _1, _2, _3 sebagai argumen (mis. _1(x, y...) = x , _2(x, y, z...) = y dll), batasannya adalah untuk dapat panggilan misalnya fun(_1) untuk x -> fun(x) , fun harus secara eksplisit dibuat kompatibel dengan perpustakaan (biasanya melalui panggilan makro, untuk membuat fun menerima "tipe lambda "sebagai parameter).

Saya sangat ingin notasi lambda singkat ini tersedia di Julia.
Mengenai masalah 2f(_) desugaring ke 2*(x -> f(x)) : apakah masuk akal untuk mengubah aturan di sepanjang baris "jika aturan pertama berlaku, misalnya f(_) , lalu kembali- mengevaluasi secara rekursif aturan dengan f(_) memainkan peran _ . Ini akan memungkinkan juga misalnya f(g(_))x -> f(g(x)) , dengan "aturan tanda kurung" memungkinkan dengan mudah untuk berhenti di level yang diinginkan, misalnya f((g(_)))f(x->g(x)) .

Saya sangat suka nama "notasi singkat lambda" untuk ini. (Jauh lebih baik dari pada kari).

Saya benar-benar lebih suka kesederhanaan _1 , _2 , _3 jika lambda multi-argumen Anda lewat. Secara umum saya sering menemukan menggunakan kembali nama variabel dalam lingkup yang sama dapat membingungkan ... dan memiliki _ menjadi x dan y dalam _ ekspresi yang sama_ sepertinya membingungkan.

Saya telah menemukan bahwa skala singkat yang sama _ -syntax telah menyebabkan sedikit kebingungan (lihat semua penggunaan _ dalam skala ).

Selain itu, seringkali Anda ingin melakukan:

x -> f(x) + g(x)

atau serupa, dan saya rasa saya akan terkejut jika hal berikut tidak berhasil:

f(_) + g(_)

Anda juga mungkin ingin mengganti urutan argumen:

x, y -> f(y, x)
f(_2, _1)  # can't do with other suggested _ syntax

Saya pikir akan baik-baik saja untuk sintaks untuk memungkinkan penomoran argumen anonim eksplisit ( _1 , _2 , _3 ... dll.), Tetapi masalah utamanya masih tetap ada : kapan tepatnya Anda mempromosikan fungsi yang diterapkan sebagian ke lambda singkat? Dan apa sebenarnya tubuh lambda itu? Saya mungkin akan melakukan kesalahan pada sisi eksplisit (dengan pembatas) daripada secara implisit menggunakan semacam aturan promosi yang kompleks. Apa seharusnya

foo(_1, _1 + _2  + f(_1, v1) + g(_2, v3), _3 * _2, v2) + g(_4, v4) +
 f(_2, v2) + g(_3, v5) + bar(_1, v6)

sebenarnya maksudnya? Menggunakan pembatas (saya akan menggunakan λ ) semuanya lebih jelas:

λ(foo(_1, λ(_1 + _2)  + λ(f(_1, v1) + g(_2, v3)), _3 * _2, v2) + g(_4, v4)) + 
λ(f(_2, v2) + g(_3, v5) + bar(_1, v6))

Ini jelas adalah MethodError: + has no method matching +(::Function, ::Function) , tetapi setidaknya saya dapat membedakannya dari cara penulisannya.

Saya benar-benar berpikir @StefanKarpinski mungkin

Ini jelas merupakan pertukaran antara kesederhanaan vs umum dan keterbacaan. Tentu saja tidak ada gunanya memperkenalkan sesuatu yang kurang ringkas dari notasi -> . Tetapi saya juga berpikir bahwa pembatas tampaknya bermanfaat.

Mungkin tidak cukup singkat, tapi bagaimana dengan versi awalan -> yang menangkap argumen _ ? Misalnya

(-> 2f(_) + 1)

Saya kira bentuk awalan harus memiliki prioritas yang cukup rendah. Ini mungkin benar-benar memungkinkan untuk meninggalkan tanda kurung dalam beberapa kasus, misalnya

map(->_ + 1, x)

Saat ini saya bermain-main dengan menerapkan https://github.com/JuliaLang/julia/issues/5571#issuecomment -157424665

Sebagai makro, yang mengubah semua kejadian seperti itu di baris.
Bagian yang sulit adalah menerapkan prioritas.

Saya mungkin tidak akan menyelesaikannya dalam 12 jam ke depan karena sudah waktunya pulang di sini
(mungkin dalam 24 mendatang, tapi saya mungkin harus pergi)
Bagaimanapun, setelah itu selesai kita bisa bermain dengannya.

Yang aneh yang keluar dari https://github.com/JuliaLang/julia/issues/5571#issuecomment -157424665

  • f(_,_)x,y -> f(x,y) (ini masuk akal)
  • f(_,2_) → ??

    • f(_,2_)x,y -> f(x,2y) (masuk akal)

    • f(_,2_)x-> f(x,y->2y) (yang menurut saya disarankan oleh aturan, dan apa yang dihasilkan prototipe saya)

Tapi saya tidak yakin saya melakukannya dengan benar.

Jadi inilah prototipe saya.
http://nbviewer.ipython.org/gist/oxinabox/50a1e17cfb232a7d1908

Nyatanya itu pasti gagal dalam beberapa tes.

Tidaklah mungkin untuk mempertimbangkan bracketing di lapisan AST saat ini - sering kali (selalu?) Sudah diselesaikan.

Masih cukup bermain dengan saya pikir

Beberapa aturan dari magrittr di R yang mungkin berguna:

Jika rantai dimulai dengan. , ini adalah fungsi anonim:

. %>% `+`(1)

sama dengan fungsi (x) x + 1

Ada dua mode rantai:
1) Merangkai dengan memasukkan sebagai argumen pertama, serta setiap titik yang muncul.
2) Merangkai hanya ke titik-titik yang muncul.

Mode default adalah mode 1. Namun, jika titik muncul dengan sendirinya sebagai argumen ke fungsi yang dirantai, maka magrittr beralih ke mode 2 untuk langkah tersebut dalam rangkaian.

Begitu

2 %>% `-`(1) 

adalah 2 - 1,

dan

1 %>% `-`(2, . )

juga 2 - 1

Mode 2 juga dapat ditentukan dengan membungkus dalam tanda kurung:

2 %>% { `-`(2, . - 1) }

akan sama dengan 2 - (2 - 1).

Juga hanya catatan bahwa dapat dengan cerdas beralih antara mode 1 dan mode 2 hampir sepenuhnya menyelesaikan masalah yang Julia tidak terlalu konsisten tentang memiliki argumen yang kemungkinan besar akan dirantai di posisi pertama. Saya juga lupa bahwa untuk mencatat bahwa tanda kurung dapat digunakan untuk mengevaluasi sepotong kode. Berikut adalah contoh dari manual magrittr:

iris%>%
{
n <- sampel (1:10, size = 1)
H <- kepala (., N)
T <- ekor (., N)
rbind (H, T)
}%>%
ringkasan

Ini hanya ide setengah terbentuk saat ini, tetapi saya ingin tahu apakah ada cara agar kita bisa menyelesaikan masalah "lambda singkat" dan "variabel dummy" pada saat yang sama dengan memodifikasi konstruktor Tuple sehingga nilai yang hilang mengembalikan lambda yang mengembalikan Tuple bukan Tuple? Jadi, (_, 'b', _, 4) akan mengembalikan (x, y) -> (x, 'b', y, 4) .

Kemudian, jika kita secara halus mengubah semantik pemanggilan fungsi sehingga foo(a, b) berarti "terapkan foo ke Tuple (a, b) atau jika argumennya adalah fungsi, maka terapkan foo ke Tupel yang dikembalikan oleh fungsi ". Ini akan menghasilkan foo(_, b, c)(1) setara dengan apply(foo, ((x) -> (x, b, c))(1)) .

Saya pikir ini masih belum menyelesaikan masalah notasi infix, tetapi secara pribadi saya akan senang dengan lambda singkat yang hanya bekerja dengan pemanggilan fungsi dalam tanda kurung. Lagi pula, 1 + _ selalu dapat ditulis ulang +(1, _) jika benar-benar diperlukan.

@jballanc Bagaimanapun, konstruksi tuple dan aplikasi fungsi adalah dua konsep yang sangat berbeda. Setidaknya, kecuali pemahaman saya tentang semantik julia benar-benar cacat.

@samuela Yang saya maksud adalah bahwa foo(a, b) setara dengan foo((a, b)...) . Artinya, argumen ke suatu fungsi dapat secara konseptual dianggap sebagai Tupel, bahkan jika Tupel tidak pernah dibangun dalam praktiknya.

@Saya sudah mencoba membaca diskusi ini, tetapi terlalu lama bagi saya untuk melacak semua yang telah dikatakan - jadi maaf jika saya mengulangi lebih dari yang diperlukan di sini.

Saya hanya ingin memberikan suara untuk membuat |> melengkapi do "ajaib". Sejauh yang saya bisa lihat, cara termudah untuk melakukannya adalah membiarkannya begitu saja

3 |> foo == foo(3) # or foo() instead of just foo, but it would be nice if the parentheses were optional
3 |> foo(1) == foo(1, 3)
3 |> foo(1,2) == foo(1,2,3)

Dengan kata lain, a |> f(x) lakukan pada argumen _last_ apa yang dilakukan f(x) do; a; end pada _first_. Ini akan segera membuatnya kompatibel dengan map , filter , all , any et. al., tanpa menambahkan kompleksitas pelingkupan parameter _ , dan mengingat sintaks do sudah ada, saya rasa ini tidak menempatkan beban konseptual yang tidak masuk akal pada pembaca kode.

Motivasi utama saya untuk menggunakan operator pipa seperti ini adalah pipa pengumpulan (lihat # 15612), yang menurut saya merupakan konstruksi yang sangat kuat, dan yang mulai berkembang dalam banyak bahasa (menunjukkan bahwa itu adalah fitur yang diinginkan orang, dan satu mereka akan mengerti).

@malmaud Bagus! Saya suka itu sudah mungkin: D

Namun, perbedaan keterbacaan antara kedua varian ini sangat besar:

# from Lazy.jl
@> x g f(y, z)

# if this became a first-class feature of |>
x |> g |> f(y, z)

Saya pikir masalah keterbacaan utama adalah bahwa tidak ada petunjuk visual untuk mengetahui di mana batas antara ekspresi berada - spasi di x g dan g f(x, akan secara signifikan memengaruhi perilaku kode, tetapi spasi di f(x, y) tidak akan.

Karena @>> sudah ada, seberapa mungkin menambahkan perilaku ini ke |> dalam 0,5?

(Saya tidak ingin menghabiskan terlalu banyak energi pada notasi operator, jadi jangan abaikan proposal ini hanya pada masalah notasi. Namun, kita dapat mencatat bahwa "perpipaan" sepertinya merupakan gagasan alami untuk ini (cf istilah "pipa koleksi"), dan misalnya F # sudah menggunakan |> untuk ini, meskipun tentu saja ada semantik yang sedikit berbeda karena fungsi F # berbeda dari fungsi Julia.)

Ya, pasti saya setuju di depan keterbacaan. Secara teknis tidak akan menantang untuk membuat |> berperilaku seperti yang Anda gambarkan di 0,5, ini hanya pertanyaan desain.

Demikian pula, dimungkinkan untuk membuat fungsi makro parse @>> Lazy.jl dirantai oleh |> .

Hm. Saya akan mulai mengerjakan PR untuk Lazy.jl, tetapi itu tidak berarti saya ingin ini tidak berada di 0.5 :) Saya rasa saya tidak cukup tahu tentang parser Julia dan bagaimana mengubah perilaku |> untuk membantu, kecuali saya mendapatkan bimbingan yang cukup ekstensif.

Saya tidak berpikir saya menyebutkan di utas ini, tetapi saya memiliki paket rantai lain, ChainMap.jl. Itu selalu menggantikan menjadi _, dan secara kondisional dimasukkan ke dalam argumen pertama. Ia juga mencoba untuk mengintegrasikan pemetaan. Lihat https://github.com/bramtayl/ChainMap.jl

Jadi daftar kami saat ini dari berbagai upaya dll
Saya pikir ada baiknya orang memeriksa ini, (idealnya sebelum berpendapat, tapi w / e)
semuanya sedikit berbeda.
(Saya mencoba untuk memesan secara kronologis).

Paket

Prototipe Non-paket

Terkait:


Mungkin ini harus diedit ke salah satu posting teratas.

diperbarui: 2020-04-20

Ini lebih merupakan eksperimen sistem jenis daripada upaya sebenarnya untuk mengimplementasikan aplikasi parsial, tetapi inilah yang aneh: https://gist.github.com/fcard/b48513108a32c13a49a387a3c530f7de

pemakaian:

include("partial_underscore_generated.jl")
using GeneratedPartial

const sub = partialize(-)
sub(_,2)(1) == 1-2
sub(_,_)(1,2) == 1-2
sub(_,__)(1)(2) == 1-2
sub(__,_)(2)(1) == 1-2 #hehehe

# or
<strong i="8">@partialize</strong> 2 Base.:+ # evily inserts methods in + and allows partializations for 2 arguments
(_+2)(1) == 1+2

# fun:
sub(1+_,_)(2,3) == sub(1+2,3)
sub(1+_,__)(2)(3) == sub(1+2,3)
(_(1)+_)(-,1) == -1+1

# lotsafun:
appf(x::Int,y::Int) = x*y
appf(f,x) = f(x)
<strong i="9">@partialize</strong> 2 appf

appf(1+_,3)(2) == appf(1+2,3)
appf(?(1+_),3) == appf(x->(1+x), 3)
appf(?sub(_,2),3) == appf(x->x-2,3) # I made a method *(::typeof(?),::PartialCall), what of it!!?

# wooooooooooooooooooooooooooooooooo
const f = sub
f(_,f(_,f(_,f(_,f(_,f(_,f(_,f(_,f(_,_)))))))))(1,2,3,4,5,6,7,8,9,10) == f(1,f(2,f(3,f(4,f(5,f(6,f(7,f(8,f(9,10)))))))))
f(_,f(__,f(___,f(____,f(_____,f(______,f(_______,f(________,f(_________,__________)))))))))(1)(2)(3)(4)(5)(6)(7)(8)(9)(10) == f(1,f(2,f(3,f(4,f(5,f(6,f(7,f(8,f(9,10)))))))))

# this answers Stefan's concern (which inspired me to make this hack in the first place)
#
#    const pmap = partialize(map)
#    map(f(_,a),   v) == map(x->f(x,a), v)
#    pmap(?f(_,a), v) == map(x->f(x,a), v)
#    pmap(f(_,a),  v) == x->map(f(x,a), v)
#
# it adds a few other issues, of course...


Tentu saja bukan proposal implementasi, tapi menurut saya menyenangkan untuk dimainkan, dan mungkin seseorang bisa mendapatkan setengah ide bagus darinya.

PS Lupa menyebutkan bahwa @partialize juga berfungsi dengan literal dan rentang array integer:

<strong i="15">@partialize</strong> 2:3 Base.:- # partialized for 2 and 3 arguments!

(_-_-_)(1,2,3) == -4
(_-_+_)(1,2,3) == +2

Oke, saya telah memikirkan tentang komposisi fungsi, dan meskipun IMO jelas dalam kasus SISO, saya rasa saya sedang memikirkan bagaimana saya menggunakan MISO Julia (quasi-MIMO?) Dalam blok kode berantai kecil.

Saya suka REPL. Banyak. Ini lucu, keren, ini memungkinkan Anda bereksperimen seperti MATLAB atau python atau cangkangnya. Saya suka mengambil kode dari paket atau file dan menyalin-tempelnya ke REPL, bahkan beberapa baris kode. REPL mengevaluasi itu di setiap baris dan menunjukkan kepada saya apa yang terjadi.

Ia juga mengembalikan / mendefinisikan ans setelah setiap ekspresi. Setiap pengguna MATLAB mengetahuinya (meskipun pada tahap ini, ini adalah argumen yang buruk!). Mungkin sebagian besar pengguna Julia pernah melihat / menggunakannya sebelumnya. Saya menggunakan ans dalam situasi ganjil yang saya mainkan dengan sesuatu yang bijak, menyadari saya ingin menambahkan langkah lain ke apa yang saya tulis di atas. Saya tidak suka bahwa menggunakannya agak merusak, jadi saya cenderung menghindarinya bila memungkinkan, tetapi proposal _every_ di sini berurusan dengan masa hidup kembali hanya dari satu langkah komposisi.

Bagi saya, _ menjadi ajaib itu hanya _odd_, tapi saya mengerti bahwa banyak orang mungkin tidak setuju. Jadi, jika saya _ ingin_ menyalin-tempel kode dari paket ke REPL dan melihatnya berjalan, dan jika saya ingin sintaks yang tidak tampak ajaib, maka saya dapat mengusulkan:

<strong i="12">@repl_compose</strong> begin
   sin(x)
   ans + 1
   sqrt(ans)
end

Jika fungsi mengembalikan beberapa output, saya dapat memasukkan ans[1] , ans[2] , dll di baris berikutnya. Model ini sudah cocok dengan model komposisi tingkat tunggal, model MISO Julia, dan sudah _ sintaks Julia yang sangat standar_, hanya saja tidak dalam file.

Makro mudah diimplementasikan - cukup ubah Expr(:block, exprs...) menjadi Expr(:block, map(expr -> :(ans = $expr), exprs) (juga let ans pada awalnya, dan mungkin ada versi yang membuat fungsi anoymous yang membutuhkan masukan atau sesuatu?). Itu tidak akan _have_ untuk tinggal di basis (meskipun, REPL dibangun ke dalam Julia, dan itu semacam itu).

Bagaimanapun, hanya perspektif saya! Ini adalah utas panjang, yang sudah lama tidak saya lihat!

Ia juga mengembalikan / mendefinisikan ans setelah setiap ekspresi. Setiap pengguna MATLAB mengetahuinya (meskipun pada tahap ini, ini adalah argumen yang buruk!).

Sebenarnya, argumen lainnya adalah jika _ digunakan dalam rangkaian fungsi, maka REPL juga harus mengembalikan _ daripada ans (bagi saya, ini sudah cukup untuk menghapus sihir").

Ada cukup banyak preseden untuk menggunakan _ sebagai "nilainya" dalam bahasa. Tentu saja, itu bertentangan dengan gagasan yang diusulkan untuk menggunakan _ sebagai nama yang membuang tugas dan untuk lambda terser.

Saya cukup yakin ini ada di suatu tempat di Lazy.jl sebagai <strong i="5">@as</strong> and begin ...

Ide menggunakan . untuk merangkai telah dibuang di awal percakapan, tetapi memiliki sejarah kegunaan yang panjang dan akan meminimalkan kurva pembelajaran untuk pengadopsi dari bahasa lain. Alasan penting bagi saya adalah karena

type Track
  hit::Array{Hit}
end
type Event
  track::Array{Track}
end

event.track[12].hit[43]

memberi saya hit ke-43 dari trek ke-12 dari sebuah peristiwa ketika track dan hit adalah array sederhana, jadi

event.getTrack(12).getHit(43)

harus memberi saya hal yang sama jika harus dilayani secara dinamis. Saya tidak ingin mengatakannya

getHit(getTrack(event, 12), 43)

Semakin dalam Anda melangkah semakin buruk. Karena ini adalah fungsi sederhana, itu membuat argumen lebih luas daripada fungsi rantai (a la Spark).

Saya menulis ini sekarang karena saya baru belajar tentang sifat Rust , yang bisa menjadi solusi yang baik untuk Julia untuk alasan yang sama. Seperti Julia, Rust hanya memiliki data structs (Julia type ), tetapi kemudian mereka juga memiliki impl untuk mengikat fungsi ke nama struct . Sejauh yang saya tahu, itu gula sintaksis murni, tetapi memungkinkan notasi titik yang saya jelaskan di atas:

impl Event {
  fn getTrack(&self, num: i32) -> Track {
    self.track[num]
  }
}

impl Track {
  fn getHit(&self, num: i32) -> Track {
    self.track[num]
  }
}

yang di Julia bisa

impl Event
  function getTrack(self::Event, num::Int)
    self.track[num]
  end
end

impl Track
  function getHit(self::Track, num::Int)
    self.hit[num]
  end
end

Sintaks yang diusulkan di atas tidak melakukan interpretasi apa pun dari self : ini hanya argumen fungsi, jadi seharusnya tidak ada konflik dengan beberapa pengiriman. Jika Anda ingin melakukan interpretasi minimal self , Anda dapat membuat tipe argumen pertama tersirat, sehingga pengguna tidak perlu mengetik ::Event dan ::Track di setiap fungsi, tetapi hal yang menyenangkan tentang tidak melakukan interpretasi apa pun adalah bahwa "metode statis" hanyalah fungsi di impl yang tidak memiliki self . (Rust menggunakannya untuk pabrik new .)

Tidak seperti Rust, Julia memiliki hierarki pada types . Itu juga bisa memiliki hierarki serupa pada impls untuk menghindari duplikasi kode. OOP standar dapat dibuat dengan membuat hierarki data type dan metode impl persis sama, tetapi pencerminan ketat ini tidak diperlukan dan dalam beberapa kasus tidak diinginkan.

Ada satu poin penting dengan ini: misalkan saya menamai fungsi saya track dan hit di impl , daripada getTrack dan getHit , sehingga mereka bertentangan dengan array track dan hit di type . Lalu akankah event.track mengembalikan array atau fungsinya? Jika Anda segera menggunakannya sebagai fungsi, itu bisa membantu untuk menghilangkan keraguan, tetapi types dapat menampung Function objek. Mungkin hanya menerapkan aturan selimut: setelah titik, pertama periksa sesuai impl , kemudian periksa yang sesuai type ?

Setelah dipikir-pikir, untuk menghindari dua "paket" untuk objek yang secara konseptual sama ( type dan impl ), bagaimana dengan ini:

function Event.getTrack(self, num::Int)
  self.track[num]
end

untuk mengikat fungsi getTrack ke instance tipe Event sedemikian rupa

myEvent.getTrack(12)

menghasilkan bytecode yang sama dengan fungsi yang diterapkan ke (myEvent, 12) ?

Apa yang baru adalah sintaks nama-titik-fungsi nama setelah kata kunci function dan bagaimana itu ditafsirkan. Ini masih memungkinkan untuk beberapa pengiriman, seperti Python self jika argumen pertama sama dengan tipe yang terikat (atau dibiarkan tersirat, seperti di atas), dan memungkinkan untuk "metode statis" jika argumen pertama tidak ada atau diketik berbeda dari tipe yang mengikatnya.

@jpivarski Apakah ada alasan Anda berpikir sintaks titik (yang, dengan membaca utas ini, memiliki banyak kerugian) lebih baik daripada beberapa konstruksi lain yang memungkinkan perangkaian? Saya masih berpikir membuat sesuatu seperti do tetapi untuk argumen terakhir , didukung oleh beberapa bentuk sintaks perpipaan (misalnya |> ) akan menjadi cara terbaik untuk maju:

event |> getTrack(12) |> getHit(43)

Alasan utama saya dapat melihat bahwa sesuatu seperti pendekatan Rust bisa lebih baik adalah karena secara efektif menggunakan sisi kiri sebagai namespace untuk fungsi, jadi Anda mungkin dapat melakukan hal-hal seperti parser.parse tanpa bertentangan dengan yang ada Fungsi Julia Base.parse . Saya akan mendukung penyediaan proposal Rust dan perpipaan gaya Hack.

@tlycken Itu adalah sintaks yang ambigu, tergantung pada prioritas.
Mengingat Precidence of |> vs call mungkin membingungkan, karena tidak memberikan petunjuk apa pun.
(Juga tidak disarankan beberapa opsi lain.)

Mempertimbangkan

foo(a,b) = a+b
foo(a) = b -> a-b

2 |> foo(10) == 12   #Pipe Precedence > Call Precedence 
2 |> foo(10) == 8     #Pipe Precedence < Call Precedence   

@oxinabox Saya sebenarnya tidak menyarankannya untuk menjadi "hanya" operator biasa, melainkan elemen sintaks bahasa; 2 |> foo(10) desugars menjadi foo(10, 2) hampir sama dengan foo(10) do x; bar(x); end desugars menjadi foo(x -> bar(x), 10) . Itu menyiratkan presedensi pipa di atas call precedence (yang, menurut saya, adalah yang paling masuk akal).

Hanya pada subjek sintaks, . kurang menonjol secara visual dan tentu saja lebih standar daripada |> . Saya dapat menulis rangkaian fungsi yang lancar yang dipisahkan oleh . tanpa spasi (masing-masing satu karakter) dan siapa pun dapat membacanya; dengan |> , saya harus menambahkan spasi (masing-masing empat karakter) dan itu akan menjadi visual speedbump bagi kebanyakan programmer. Analogi untuk skrip shell | bagus, tetapi tidak langsung dikenali karena > .

Apakah saya membaca utas ini dengan benar bahwa argumen melawan titik adalah bahwa itu ambigu apakah itu harus mendapatkan datum anggota dari type atau fungsi anggota dari impl (saran pertama saya) atau fungsinya namespace (saran kedua saya)? Dalam saran kedua, fungsi yang ditentukan dalam ruang nama fungsi yang dibuat oleh type harus ditentukan _setelah_ type ditentukan, sehingga dapat menolak untuk menutupi datum anggota di sana.

Menambahkan kedua titik namespace (saran kedua saya) dan |> akan baik-baik saja bagi saya; mereka agak berbeda dalam tujuan dan efek, terlepas dari kenyataan bahwa keduanya dapat digunakan untuk rangkaian yang lancar. Namun, |> seperti yang dijelaskan di atas tidak sepenuhnya simetris dengan do , karena do membutuhkan argumen yang diisikannya untuk menjadi sebuah fungsi. Jika Anda mengatakan event |> getTrack(12) |> getHit(43) , maka |> berlaku untuk non-fungsi ( Events dan Tracks ).

Jika Anda mengatakan event |> getTrack(12) |> getHit(43) , maka |> berlaku untuk non-fungsi ( Events dan Tracks ).

Sebenarnya, tidak - ini berlaku untuk fungsi mantera _di sebelah kanannya_ dengan memasukkan operan kirinya sebagai argumen terakhir ke pemanggilan fungsi. event |> getTrack(12) adalah getTrack(12, event) karena apa yang ada di sebelah kanan, bukan karena apa yang ada di sebelah kiri.

Ini harus berarti a) prioritas di atas panggilan fungsi (karena ini adalah penulisan ulang panggilan), dan b) urutan aplikasi kiri-ke-kanan (untuk menjadikannya getHit(43, getTrack(12, event)) daripada getHit(43, getTrack(12), event) ) .

Tapi tanda tangan getTrack's adalah

function getTrack(num::Int, event::Event)

jadi jika event |> getTrack(12) menyisipkan event ke dalam getTrack's argumen terakhir, ini menempatkan Event ke dalam argumen kedua, bukan Function . Saya baru saja mencoba yang setara dengan do dan argumen pertama, dan Julia 0.4 mengeluh bahwa argumen tersebut harus berupa fungsi. (Mungkin karena do event end ditafsirkan sebagai fungsi yang mengembalikan event , bukan event itu sendiri.)

Rangkaian fungsi tampaknya, bagi saya, menjadi masalah terpisah dari banyak hal yang sedang dibahas seputar sintaks titik ( . ). Misalnya, @jpivarski , Anda sudah dapat mencapai banyak hal yang Anda sebutkan dari Rust di Julia tanpa fitur baru:

type TownCrier
  name::AbstractString
  shout::Function

  function TownCrier(name::AbstractString)
    self = new(name)
    self.shout = () -> "HELLO, $(self.name)!"
    self
  end
end

tc = TownCrier("Josh")
tc.shout()                                #=> "HELLO, Josh!"
tc.name = "Bob"
tc.shout()                                #=> "HELLO, Bob!"

Tanpa mencoba menggagalkan percakapan terlalu banyak, saya menyarankan bahwa yang benar-benar perlu kita selesaikan adalah bagaimana melakukan fungsi kari yang efisien di Julia. Pertanyaan tentang bagaimana menentukan posisi untuk argumen dalam rantai fungsi akan mencair jika kita memiliki cara yang baik untuk mengatur fungsi. Selain itu, konstruksi seperti di atas akan lebih bersih jika badan fungsi dapat ditentukan dan hanya dikeringkan dengan "diri" pada konstruksi.

@andyferris Saya telah menggunakan Python dan saya sangat suka _ mengacu pada hasil ekspresi sebelumnya. Itu tidak berfungsi di dalam fungsi. Akan sangat bagus jika kita bisa membuatnya berfungsi di mana saja: di dalam blok start, fungsi, dll.

Saya pikir ini benar-benar bisa menggantikan rantai. Ini tidak meninggalkan ambiguitas tentang urutan argumen. Sebagai contoh,

begin
    1
    vcat(_, 2)
    vcat(3, _)
end

# [3, 1, 2]

Seperti yang disebutkan @MikeInnes , ini sudah tersedia di @_ di Lazy.jl (dan meskipun awalnya tidak berfungsi seperti itu, ChainMap.jl juga menggunakan rangkaian jenis ini sekarang).

Mudah-mudahan, ini bisa bekerja sama dengan titik fusi, setidaknya di dalam blok

begin
    [1, 2, 3]
    .+(_, 2)
    .*(_, 2)
    .-(10, _)
end

atau, menggunakan sintaks @chain_map ,

begin
    ~[1, 2, 3]
    +(_, 2)
    *(_, 2)
    -(10, _)
end

Saat ini ada cara untuk merangkai fungsi dengan objek jika fungsi tersebut didefinisikan di dalam konstruktor. Misalnya, fungsi Obj.times:

type Obj
    x
    times::Function
    function Obj(x)
       this = new(x)
       this.times =  (n) -> (this.x *= n; this)
       this
    end
end

>>>Obj(2).times(3)
Obj(6,#3)

Bagaimana dengan implementasi fungsi anggota (fungsi utama) yang didefinisikan di luar definisi tipe. Misalnya, fungsi Obj.times akan ditulis sebagai:

member function times(this::Obj, n)
     this.x *= n
     return this
end

>>>Obj(2).times(3)
Obj(6,#3)

dimana member adalah kata kunci khusus untuk fungsi anggota.
Fungsi anggota memiliki akses ke data objek. Nanti, mereka akan dipanggil menggunakan titik setelah variabel objek.
Idenya adalah untuk mereproduksi perilaku fungsi yang didefinisikan di dalam konstruktor dengan menggunakan fungsi "anggota" yang ditentukan di luar definisi tipe.

Sesuatu seperti ini dilakukan di Rust dengan sintaks metode . Ini secara konseptual berbeda dari perangkaian fungsi, meskipun dapat digunakan untuk membuat perangkaian terlihat seperti pada beberapa bahasa OO. Mungkin yang terbaik adalah mengatasi masalah secara terpisah.

Saya telah membaca ini dan beberapa masalah terkait, berikut adalah proposal saya:

Rantai dasar :
in1 |> function1
Sama seperti: in1 |> function1(|>)

in2 |> function2(10)
Sama seperti: in2 |> function2(|>,10)

Lebih banyak rantai:
in1 |> function1 |> function2(10,|>)

Percabangan dan penggabungan rantai:
Cabang dua kali dengan cabang out1 , out2 :
function1(a) |out1>
function2(a,b) |out2>

Gunakan cabang out1 dan out2 :
function3(|out1>,|out2>)

Bagaimana dengan kemalasan?
Apakah kita membutuhkan sesuatu seperti konvensi function!(mutating_var) ?
Untuk fungsi malas kita bisa menggunakan function?() ...

Dan dengan menggunakan indentasi dengan benar, mudah untuk melacak ketergantungan data secara visual pada grafik panggilan terkait.

Saya baru saja bermain-main dengan pola untuk rangkaian fungsi dengan operator |> . Misalnya, definisi ini:
`` '' julia

Saring

MyFilter {F} tetap
flt :: F
akhir

function (mf :: MyFilter) (sumber)
filter (mf.flt, sumber)
akhir

function Base.filter (flt)
Filter Saya (flt)
akhir

Mengambil

MyTake yang tidak dapat diubah
n :: Int64
akhir

function (mt :: MyTake) (sumber)
ambil (sumber, mt.n)
akhir

function Base.take (n)
MyTake (n)
akhir

Peta

MyMap yang tidak dapat diubah {F}
f :: F
akhir

fungsi (mm :: MyMap) (sumber)
peta (mm.f, sumber)
akhir

function Base.map (f)
Peta Saya (f)
akhir
enable this to work: julia
1:10 |> filter (i-> i% 2 == 0) |> ambil (2) |> peta (i-> i ^ 2) |> kumpulkan
`` Essentially the idea is that functions like filter return a functor if they are called without a source argument, and then these functors all take one argument, namely whatever is "coming" from the left side of the |> . The |> `` lalu hubungkan semua fungsi ini bersama-sama.

filter dll. Juga bisa mengembalikan fungsi anonim yang membutuhkan satu argumen, tidak yakin opsi mana yang lebih berkinerja.

Dalam contoh saya, saya menimpa map(f::Any) di Base, saya tidak begitu mengerti apa yang dilakukan oleh definisi map ...

Saya baru saja menemukan pola ini, dan pandangan sekilas saya ke sekeliling tidak menunjukkan diskusi tentang hal seperti itu. Apa yang orang pikirkan? Mungkinkah ini berguna? Bisakah orang memikirkan kekurangan dari ini? Jika ini berhasil, mungkinkah desain yang ada sebenarnya cukup fleksibel untuk memungkinkan cerita rangkaian yang cukup komprehensif?

Ini tampaknya tidak bisa diterapkan untuk fungsi arbitrer, hanya yang MyF telah ditentukan?

Ya, ini hanya berfungsi untuk fungsi yang ikut serta. Jelas bukan solusi yang sangat umum, dan dalam beberapa hal cerita yang sama dengan vektorisasi, tetapi tetap saja, mengingat bahwa tidak semua yang kami harapkan akan membuatnya menjadi 1.0, pola ini mungkin memungkinkan sejumlah besar skenario di mana orang harus menggunakan makro sekarang.

Pada dasarnya idenya adalah bahwa fungsi seperti filter mengembalikan sebuah functor jika mereka dipanggil tanpa argumen sumber, dan kemudian functor ini semua mengambil satu argumen, yaitu apapun yang "datang" dari sisi kiri |>.

Ini, hampir persis, inti dari transduser Clojure. Perbedaan yang mencolok adalah Clojure membuat transduser di atas konsep reduksi. Singkatnya, setiap fungsi yang beroperasi pada sebuah koleksi dapat diuraikan menjadi fungsi "pemetaan" dan fungsi "pengurangan" (bahkan jika fungsi "mengurangi" hanya concat ). Keuntungan merepresentasikan fungsi collection dengan cara ini adalah Anda dapat mengatur ulang eksekusi sehingga semua "pemetaan" dapat di-pipeline (terutama bagus untuk koleksi besar). Transduser, kemudian, hanyalah ekstraksi dari "pemetaan" ini yang dikembalikan saat dipanggil tanpa koleksi untuk dioperasikan.

Tidak perlu ini menjadi terlalu rumit. Fungsi dapat memilih untuk kari dengan penutupan:

Base.map(f)    = (xs...) -> map(f, xs...)
Base.filter(f) = x -> filter(f, x)
Base.take(n)   = x -> take(x, n)

Tentu saja, ini bukanlah sesuatu yang harus dilakukan oleh sebuah paket karena ia mengubah arti metode ini untuk semua paket. Dan melakukannya sedikit demi sedikit seperti ini tidak terlalu intuitif - argumen mana yang harus mendapat prioritas?

Saya lebih suka solusi sintaksis situs panggilan seperti yang telah dibahas di atas, menurunkan f(a, _, b) menjadi x -> f(a, x, b) . Ini rumit, seperti yang dicatat dalam diskusi panjang di atas.

Tidak perlu ini menjadi terlalu rumit. Fungsi dapat memilih untuk kari dengan penutupan

Ya, saya sudah menyarankan di atas, saya hanya tidak yakin apakah ada perbedaan kinerja antara keduanya.

argumen mana yang harus mendapat prioritas?

Ya, dan kemudian kami benar-benar memiliki hal-hal seperti filter dan take , di mana dalam satu kasus kami memiliki koleksi sebagai yang pertama dan yang lain sebagai argumen terakhir ... Saya merasa bahwa setidaknya untuk operasi seperti iterator mungkin biasanya ada jawaban yang jelas untuk pertanyaan itu.

Sekali _ adalah simbol khusus yang tersedia

Ya, saya sangat setuju bahwa ada solusi yang lebih umum di luar sana, dan @malmaud mungkin saja

Tidak ada perf diff karena closures pada dasarnya hanya menghasilkan kode yang Anda tulis dengan tangan. Tetapi karena Anda baru saja membuat kari, Anda dapat menulis fungsi untuk melakukannya untuk Anda ( curry(f, as...) = (bs...) -> f(as..., bs...) ). Itu menangani peta dan filter; ada juga proposal di masa lalu untuk mengimplementasikan curry yang mengimplementasikan nilai sentinel seperti curry(take, _, 2) .

Saya datang ke sini, karena saya sedang belajar tiga bahasa: D, Elixir dan sekarang Julia. Di D ada sintaks pemanggilan fungsi seragam , seperti di Rust, di Elixir Anda memiliki operator pipa . Keduanya pada dasarnya menerapkan jenis rangkaian fungsi yang disarankan di sini dan saya sangat menyukai fitur ini dalam kedua bahasa, karena mudah dipahami, tampaknya mudah diterapkan, dan dapat membuat kode menggunakan aliran dan jenis pipeline data lainnya jauh lebih mudah dibaca.

Sejauh ini saya hanya melihat tutorial singkat tentang sintaks julia, tetapi saya langsung mencari fitur ini di Google, karena saya berharap Julia juga akan mendapatkan yang seperti ini. Jadi saya rasa ini adalah +1 untuk permintaan fitur ini dari pihak saya.

Hai teman-teman,

Izinkan saya memberi +1 pada permintaan fitur ini. Ini sangat dibutuhkan. Perhatikan sintaks Scala berikut ini.

Array(1,2,3,4,5)
  .map(x => x+1)
  .filter(x => x > 5)
  .reduce(_ + _)

Orang-orang yang telah menggunakan Spark atau alat data besar berbasis MapReduce lainnya akan sangat akrab dengan sintaks ini dan akan menulis pekerjaan yang besar dan rumit dengan cara ini. Bahkan R, yang relatif kuno, memungkinkan hal berikut.

c(1,2,3,4,5) %>%
  {. + 1} %>%
  {.[which(. > 5)]} %>%
  sum

Perhatikan penggunaan blok kode yang cerdik sebagai pengganti pemrograman fungsional yang tepat - bukan yang tercantik, tetapi kuat. Di Julia, saya bisa melakukan hal berikut.

[1,2,3,4,5] |> 
  _ -> map(__ -> __ + 1, _) |>
  _ -> filter(__ -> __ < 5, _) |>
  _ -> reduce(+, _)

Tapi ini mengerikan dan jelek. Jika kita tidak dapat memiliki kode berorientasi objek seperti Scala, operator pipa menjadi sangat penting. Solusi paling sederhana adalah pipa dimasukkan ke dalam _first argument_, kecuali wildcard seperti _ digunakan secara eksplisit, tapi ini hanya masuk akal jika map diubah untuk mengambil struktur data sebagai argumen dan fungsi pertama sebagai yang kedua.

Juga harus ada beberapa yang setara dengan Array(1,2,3).map(_ + 1) Scala untuk menghindari _ -> _ + 1 berlebihan dan sintaks yang serupa. Saya suka ide di atas di mana [1,2,3] |> map(~ + 1, _) diterjemahkan menjadi map(~ -> ~ + 1, [1,2,3]) . Terima kasih telah melihat.

Untuk yang terakhir, kami memiliki penyiaran dengan sintaks ringkas [1, 2, 3] .+ 1 Ini cukup membuat ketagihan. Sesuatu seperti itu untuk pengurangan (dan mungkin penyaringan) akan sangat keren, tapi sepertinya pertanyaan besar.

Ini adalah poin yang masuk akal untuk dicatat bahwa kedua piping dan do bertarung untuk argumen fungsi pertama.

Saya akan mengingatkan yang baru datang ke utas, yang kita miliki,
bukan satu, bukan dua, tapi LIMA paket menyediakan ekstensi untuk fungsionalitas perpipaan SISO dasar julia, sesuai dengan sintaks yang disarankan.
lihat daftarnya di: https://github.com/JuliaLang/julia/issues/5571#issuecomment -205754539

Ini adalah poin yang masuk akal untuk dicatat bahwa baik perpipaan dan memang berjuang untuk argumen fungsi pertama.

Jika kita mendapatkan fungsionalitas perpipaan tambahan di basis, itu bukan posisi yang ditandai dengan _ dll.
Kemudian saya akan berpikir itu akan menambahkan argumen ke posisi akhir, bukan yang pertama.
yang akan membuatnya lebih seperti "kue pura-pura / aplikasi sebagian"

Posting saya di atas dimaksudkan sebagai contoh sederhana yang dirancang untuk menggambarkan masalah sintaks yang dimaksud.

Pada kenyataannya, seringkali ratusan operasi digunakan dalam satu rantai, banyak di antaranya tidak standar. Bayangkan bekerja dengan data besar bahasa natural. Anda menulis urutan transformasi yang mengambil serangkaian karakter, menyaring karakter Cina, memisahkan dengan spasi, menyaring kata-kata seperti "the", mengubah setiap kata menjadi bentuk "standar" melalui perangkat lunak kotak hitam yang digunakan oleh web Anda server, tambahkan informasi tentang seberapa umum setiap kata melalui alat kotak hitam lain, jumlah bobot untuk setiap kata unik, 100 operasi lain, dll.

Ini adalah situasi yang saya pertimbangkan. Melakukan operasi semacam itu tanpa menggunakan pemanggilan atau pipa metode tidak dapat dilakukan karena ukurannya yang besar.

Saya tidak tahu apa desain terbaik, saya hanya mendorong semua orang untuk mempertimbangkan kasus penggunaan dan sintaks yang lebih rumit daripada yang ada saat ini.

Ini harus bekerja di Juno dan Julia 0.6

`` {julia}
menggunakan LazyCall
@lazycall_methods Base Generator
@lazycall_methods Iterator filter

menggunakan ChainRecursive
start_chaining ()


```{julia}
[1, 2, 3, 4, 5]
<strong i="14">@unweave</strong> ~it + 1
Base.Generator(it)
<strong i="15">@unweave</strong> ~it < 5
Iterators.filter(it)
reduce(+, it)

Saya memiliki pertanyaan tentang beberapa sintaks yang saya lihat di komentar tentang masalah ini:
https://stackoverflow.com/questions/44520097/method-chaining-in-julia

@somedadaism , masalah adalah untuk masalah dan bukan untuk "mengiklankan" pertanyaan stack-overflow. Juga, Julia-people sangat aktif di SO dan (terlebih lagi) di https://discourse.julialang.org/. Saya akan sangat terkejut jika Anda tidak mendapatkan jawaban atas sebagian besar pertanyaan di sana dengan sangat cepat. Dan, selamat datang di Julia!

Astaga, sulit dipercaya betapa rumitnya hal ini. 1 untuk beberapa sintaks yang layak. Bagi saya, penggunaan utama perpipaan juga bekerja dengan data (bingkai). Pikirkan dplyr. Secara pribadi saya tidak terlalu peduli tentang lewat pertama / terakhir sebagai default tetapi saya kira sebagian besar pengembang paket akan memiliki fungsinya menerima data sebagai argumen pertama - dan bagaimana dengan argumen opsional? 1 untuk sesuatu seperti

1 |> sin |> sum(2, _)

Seperti yang telah disebutkan sebelumnya, keterbacaan dan kesederhanaan sangat penting. Saya tidak ingin melewatkan seluruh gaya dplyr / tidyverse dalam melakukan sesuatu untuk analisis data ...

Saya ingin menambahkan bahwa menurut saya sintaks multiline Elixir sangat berguna untuk operator pipa juga.

1
|> sin
|> sum(2)
|> println

Apakah setara dengan println(sum(sin(1),2))

Sekadar mencatat sebuah proposal di dunia javascript . Mereka menggunakan operator ? alih-alih _ atau ~ yang sudah kita maksudkan ( _ untuk mengabaikan sesuatu dan ~ sebagai bitwise tidak atau formular). Mengingat saat ini kami menggunakan ? yang sama dengan javascript, kami mungkin menggunakannya untuk placeholder kari juga.

Ini adalah tampilan proposal mereka (ada di javascript, tapi juga berlaku di julia :)

const addOne = add(1, ?); // apply from the left
addOne(2); // 3

const addTen = add(?, 10); // apply from the right
addTen(2); // 12

// with pipeline
let newScore = player.score
  |> add(7, ?)
  |> clamp(0, 100, ?); // shallow stack, the pipe to `clamp` is the same frame as the pipe to `add`.

const maxGreaterThanZero = Math.max(0, ...);
maxGreaterThanZero(1, 2); // 2
maxGreaterThanZero(-1, -2); // 0

Ringkasan karena saya mulai menulisnya karena alasan lain.
Lihat juga daftar saya sebelumnya paket terkait .

Mengotak-atik _ tidak melanggar dapat dilakukan di 1.x, karena https://github.com/JuliaLang/julia/pull/20328

Ini semua bermuara pada dua opsi utama (Selain status quo).
Keduanya dapat (untuk semua maksud dan tujuan) diimplementasikan dengan makro untuk menulis ulang sintaks.

Messing dengan _ untuk membuat fungsi langsung

@StefanKarpinski 's Terse Lambdas , atau sintaks serupa di mana keberadaan _ (dalam ekspresi RHS), menunjukkan bahwa seluruh ekspresi adalah fungsi anonim.

  • ini hampir bisa ditangani oleh makro lihat .

    • Satu-satunya hal yang tidak dapat dilakukan adalah (_) tidak sama dengan _ . Yang mana hanya fungsi identitas jadi tidak terlalu penting

    • Ini akan berlaku di mana-mana, jadi tidak hanya berguna dengan |> , tetapi juga misalnya dengan menulis hal-hal seperti map(log(7,_), xs) atau log(7, _).(xs) untuk mengambil log dengan basis 7 dari setiap elemen dari xs .

    • Saya pribadi menyukai ini, jika kami melakukan sesuatu.

Messing dengan |> untuk melakukan substitusi

Pilihannya meliputi:

  • Jadikan itu membuat RHS-nya bertindak seperti kari

    • sebenarnya saya pikir ini rusak, (meskipun mungkin ada versi non-breaking yang memeriksa tabel metode. Saya pikir itu malah membingungkan)

  • Jadikan _ bertindak spesial (lihat opsi di atas, dan / atau berbagai cara untuk memalsukannya melalui penulisan ulang)

    • salah satu cara untuk melakukan ini adalah memungkinkan pembuatan makro infix kemudian seseorang dapat menulis @|>@ dan menentukannya seperti yang Anda inginkan dalam paket (ini telah ditutup setelah https://github.com/JuliaLang/julia/ masalah / 11608)

    • atau berikan properti khusus itu secara intrinsik

  • Kami memiliki banyak implementasi makro untuk melakukan ini, seperti yang saya katakan lihat daftar paket terkait saya
  • Beberapa orang juga mengusulkan mengubah its untuk membuatnya (tidak seperti semua operator lain) dapat menyebabkan ekspresi pada baris sebelum terjadi tidak berakhir. JADI Anda bisa menulis
a
|> f
|>g

Daripada saat ini:

a |>
f |>
g

(Menerapkan ini dengan makro tidak mungkin tanpa tanda kurung blok. Dan tanda kurung blok sudah membuatnya berfungsi)

  • Saya pribadi tidak menyukai proposal ini karena mereka menghasilkan |> (operator yang sudah tidak disukai) super ajaib.

Sunting : seperti yang ditunjukkan @StefanKarpinski di bawah , ini sebenarnya selalu merusak perubahan.
Karena seseorang bisa bergantung pada typeof(|>) <: Function .
Dan perubahan ini akan membuatnya menjadi elemen sintaks bahasa.

Opsi bonus: tidak pernah terjadi opsi: tambahkan kari di mana-mana # 554

Itu terlalu terlambat dalam bahasa untuk menambahkan kari.
Ini akan gila melanggar, menambahkan tumpukan besar ambiguitas di mana-mana.
Dan sangat membingungkan.

Saya pikir dengan dua opsi ini pada dasarnya mencakup semua yang layak dipertimbangkan.
Saya tidak berpikir hal lain yang berwawasan telah dikatakan (yaitu tidak menghitung "+1 menginginkan ini"; atau pengulangan variabel mikro di atas).

Saya cukup tergoda untuk menghentikan |> dalam 0.7 sehingga nanti kita dapat memperkenalkannya dengan semantik yang lebih berguna dan mungkin tidak berfungsi yang menurut saya diperlukan untuk membuat pemipaan berfungsi dengan baik.

Saya cukup tergoda untuk meniadakan |> di 0.7 sehingga nanti kita dapat memperkenalkannya dengan semantik yang lebih berguna dan mungkin tidak berfungsi yang menurut saya diperlukan untuk membuat perpipaan berfungsi dengan baik.

Satu-satunya kasus yang putus dalam daftar itu adalah ketika |> membuat sisi kanannya seolah-olah sedang memasak kari.
Entah untuk memasukkan argumennya ke dalam posisi argumen pertama, atau ke posisi argumen terakhir terakhir, atau beberapa posisi tetap lainnya (mungkin masuk akal ke-2).
Tanpa menggunakan _ sebagai penanda argumen yang akan dimasukkan.

Tidak ada proposal melanggar lain yang dibuat sehingga siapa pun di utas ini menganggapnya serius. |
Saya akan terkejut jika ada definisi lain yang masuk akal namun melanggar untuk operasi itu,
yang belum ada yang menyarankan dalam hampir 4 tahun terakhir ini.

Pokoknya mencela itu tidak akan buruk.
Paket yang menggunakannya masih bisa memilikinya melalui salah satu paket makro.

Ide lain mungkin menyimpan |> dengan perilaku saat ini dan memperkenalkan fungsionalitas baru dengan nama berbeda yang tidak memerlukan penggunaan tombol shift, seperti \\ (yang tidak bahkan mengurai sebagai operator sekarang). Kami pernah membicarakan hal ini di Slack sekali, tetapi saya pikir sejarahnya mungkin hilang begitu saja.

Perpipaan sering digunakan secara interaktif, dan kemudahan pengetikan memengaruhi seberapa "ringan" rasanya saat digunakan. Satu karakter | bisa menyenangkan.

Satu karakter | bisa menyenangkan.

Secara interaktif ya, tetapi cukup dengan memilikinya di .juliarc.jl (yang sudah saya miliki sejak lama ;-p)

yang tidak membutuhkan penggunaan tombol shift

Perhatikan bahwa ini adalah properti yang sangat bergantung pada lokal. Misalnya keyboard Swedia saya telah mengirimkan sejumlah karakter untuk digeser dan kombinasi AltGr (agak mengerikan) untuk memberi ruang bagi tiga huruf lainnya.

Apakah ada tradisi menggunakan |> untuk tujuan ini? [Mathematica] (http://reference.wolfram.com/language/guide/Syntax.html) memiliki // untuk aplikasi fungsi postfix, yang seharusnya mudah diketik di sebagian besar keyboard dan mungkin tersedia, jika itu belum digunakan untuk komentar (seperti di C ++) atau divisi integer (seperti di Python).

Sesuatu dengan | di dalamnya memiliki koneksi yang bagus dengan skrip shell, meskipun jika tentu saja satu | diharapkan menjadi bitwise ATAU. Apakah || diambil untuk logika OR? Bagaimana dengan ||| ? Mengetik karakter yang sulit dijangkau tiga kali tidak jauh lebih sulit daripada mengetiknya sekali.

Apakah ada tradisi menggunakan |> untuk tujuan ini?

Saya percaya tradisi |> berasal dari keluarga bahasa ML. Terkait operator, hanya sedikit komunitas bahasa pemrograman yang telah menjelajahi ruang ini seperti yang dimiliki komunitas ML / Haskell. Sejumlah kecil contoh:

Untuk menambah daftar di atas, R menggunakan%>% - dan meskipun bahasa itu sudah kuno, saya pikir fungsionalitas pipanya dirancang dengan sangat baik. Salah satu hal yang membuatnya efektif adalah sintaks kurung kurawal, yang memungkinkan seseorang untuk menulis hal-hal seperti

x %>% { if(. < 5) { a(.) } else { b(.) } }

yang akan sedikit lebih bertele-tele di Julia karena penggunaan pernyataan akhir. Meskipun contoh saya di atas abstrak, banyak orang menggunakan sintaks yang serupa saat menulis kode praproses data. Dengan salah satu proposal saat ini sedang dibahas, dapatkah sesuatu yang serupa dengan di atas dicapai - mungkin melalui penggunaan tanda kurung?

Saya juga akan mendorong penggunaan operator pipa yang memberikan beberapa indikasi visual bahwa argumen sedang disalurkan ke depan, seperti meskipun simbol > . Ini memberikan petunjuk yang berguna untuk pemula dan mereka yang tidak terbiasa dengan pemrograman fungsional.

Meskipun penggunaan yang diusulkan dari |> tidak bertentangan dengan penggunaan umum saat ini secara sintaksis, mereka _are_ tidak kompatibel dengan |> sebagai operator - karena kebanyakan dari mereka melibatkan pemberian |> jauh lebih banyak kekuatan dari fungsi infix belaka. Bahkan jika kita yakin ingin mempertahankan x |> f |> g berarti g(f(x)) , membiarkannya sebagai operator biasa mungkin akan menghalangi peningkatan lebih lanjut. Saat mengubah |> menjadi non-operator yang menjalankan aplikasi fungsi postfix mungkin tidak merusak penggunaan _typical_ untuk aplikasi fungsi yang dirantai, itu tetap tidak diperbolehkan karena akan merusak penggunaan _atypical_ dari |> - apapun yang mengandalkannya sebagai operator. Pemutusan penggunaan atipikal masih melanggar dan oleh karena itu tidak diizinkan dalam rilis 1.x. Jika kita ingin melakukan salah satu proposal di atas dengan |> sejauh yang saya tahu, kita perlu membuat sintaks |> daripada fungsi di 1.0.

@StefanKarpinski Apakah membuat |> sintaks daripada fungsi bahkan di atas meja saat ini? Apakah mungkin untuk meletakkannya di atas meja pada waktunya untuk menempatkannya pada 1.0?

@StefanKarpinski Apakah membuat |> sintaks daripada fungsi bahkan di atas meja saat ini? Apakah mungkin untuk meletakkannya di atas meja pada waktunya untuk menempatkannya pada 1.0?

Menghentikannya di 0.7 dan menghapusnya langsung dari 1.0 ada di atas meja.
Kemudian kembalikan beberapa saat selama 1.x sebagai elemen sintaks.
Yang pada saat itu akan menjadi perubahan yang tidak terputus.

Seseorang perlu melakukannya, tetapi menurut saya itu bukan perubahan yang sangat sulit, jadi ya, itu sudah di atas meja.

Apa yang membuat |> tidak digunakan lagi? Implementasi di Lazy.jl?

x |> f dapat dihentikan menjadi f(x) .

Bagaimana kalau menolak l> tetapi pada saat yang sama memperkenalkan katakan ll> yang memiliki perilaku yang sama dengan l> ?

Jika kita hanya pergi dengan deprecation tanpa penggantian, paket yang mengandalkan perilaku saat ini pada dasarnya akan dibiarkan tanpa opsi yang baik. Jika mereka mendapatkan ekspresi yang sedikit kurang bagus sementara itu, mereka dapat melanjutkan desain mereka saat ini, tetapi kami tetap membiarkan opsi di atas tabel untuk menemukan solusi yang sangat bagus untuk l> di masa mendatang.

Ini memengaruhi ekosistem Query and friends secara besar-besaran: Saya telah membuat sistem yang sangat mirip dengan sintaks pipa di R tidyverse. Semuanya cukup komprehensif: ini mencakup file io untuk tujuh format file tabular saat ini (dengan dua lagi sangat dekat), semua operasi kueri (seperti dplyr) dan plot (tidak terlalu lama, tapi saya optimis kita dapat memiliki sesuatu yang terasa ggplot segera). Itu semua dibangun di atas implementasi l> ...

Saya harus mengatakan saya mendukung pilihan untuk sesuatu yang lebih baik untuk l> di atas meja. Ini berfungsi dengan baik untuk apa yang saya buat sejauh ini, tetapi saya dapat dengan mudah melihat pendekatan yang lebih baik. Tetapi mencela saja tampaknya merupakan langkah yang sangat radikal yang dapat menarik perhatian banyak paket.

Pilihan lainnya adalah membuat x |> f sintaks alternatif untuk f(x) . Itu akan memecahkan kode yang membebani |> tetapi memungkinkan kode yang menggunakan |> untuk rangkaian fungsi agar tetap berfungsi sambil membiarkan hal-hal terbuka untuk peningkatan tambahan selama mereka kompatibel dengan itu.

Alternatifnya adalah dengan memperkenalkan sintaksis rangkaian sintaksis baru di masa mendatang, tetapi perlu sesuatu yang saat ini merupakan kesalahan sintaks, yang merupakan hasil yang cukup tipis pada saat ini.

Alternatifnya adalah dengan memperkenalkan sintaksis rangkaian sintaksis baru di masa mendatang, tetapi perlu sesuatu yang saat ini merupakan kesalahan sintaks, yang merupakan hasil yang cukup tipis pada saat ini.

Bukankah proposal saya dari atas mengizinkan hal itu? Yaitu membuat |> kesalahan sintaks di Julia 1.0, dan membuat ||> setara dengan |> . Untuk julia 1.0 ini akan menjadi gangguan kecil untuk kode yang saat ini menggunakan |> karena seseorang harus beralih ke ||> . Tapi saya rasa itu tidak terlalu buruk, ditambah lagi itu bisa sepenuhnya otomatis. Kemudian, setelah seseorang memiliki ide bagus untuk |> , ide tersebut dapat diperkenalkan kembali ke bahasa tersebut. Pada saat itu akan ada ||> dan |> , dan saya berasumsi ||> perlahan-lahan akan memudar ke latar belakang jika setiap orang mulai mengadopsi |> . Dan kemudian, dalam beberapa tahun, julia 2.0 bisa menghapus ||> . Dalam pikiran saya, itu akan a) tidak menimbulkan masalah nyata bagi siapa pun dalam jangka waktu julia 1.0, dan b) membiarkan semua opsi di atas meja untuk solusi yang benar-benar bagus untuk |> akhirnya.

|>(x, f) = f(x)
|>(x, tuple::Tuple) = tuple[1](x, tuple[2:endof(tuple)]...) # tuple
|>(x, f, args...) = f(x, args...) # args

x = 1 |> (+, 1, 1) |> (-, 1) |> (*, 2) |> (/, 2) |> (+, 1) |> (*, 2) # tuple
y = 1 |> (+, 1, 1)... |> (-, 1)... |> (*, 2)... |> (/, 2)... |> (+, 1)... |> (*, 2)... # args

Tidak mudah untuk menulis berkali-kali tetapi dari kiri ke kanan dan tidak menggunakan makro.

function fibb_tuple(n)
    if n < 3
        return n
    end
    fibb_tuple(n-3) |> (+, fibb_tuple(n-2), fibb_tuple(n-1))
end

function fibb_args(n)
    if n < 3
        return n
    end
    fibb_args(n-3) |> (+, fibb_args(n-2), fibb_args(n-1))...
end

function fibb(n)
    if n < 3
        return n
    end
    fibb(n-3) + fibb(n-2) + fibb(n-1)
end

n = 25

println("fibb_tuple")
<strong i="8">@time</strong> fibb_tuple(1)
println("fibb_args")
<strong i="9">@time</strong> fibb_args(1)
println("fibb")
<strong i="10">@time</strong> fibb(1)

println("tuple")
<strong i="11">@time</strong> fibb_tuple(n)
println("args")
<strong i="12">@time</strong> fibb_args(n)
println("fibb")
<strong i="13">@time</strong> fibb(n)
fibb_tuple
  0.005693 seconds (2.40 k allocations: 135.065 KiB)
fibb_args
  0.003483 seconds (1.06 k allocations: 60.540 KiB)
fibb
  0.002716 seconds (641 allocations: 36.021 KiB)
tuple
  1.331350 seconds (5.41 M allocations: 151.247 MiB, 20.93% gc time)
args
  0.006768 seconds (5 allocations: 176 bytes)
fibb
  0.006165 seconds (5 allocations: 176 bytes)

|>(x, tuple::Tuple) = tuple[1](x, tuple[2:endof(tuple)]...) jelek.
|>(x, f, args...) = f(x, args...) membutuhkan lebih banyak surat tetapi cepat.

Saya pikir mengizinkan subject |> verb(_, objects) menyukai sintaks sebagai verb(subject, objects) berarti mendukung SVO (tapi default Julia adalah VSO atau VOS). Namun Julia mendukung mutltidipatch sehingga subjek dapat menjadi subjek. Saya pikir kita harus mengizinkan sintaks (subject1, subject2) |> verb(_, _, object1, object2) like sebagai verb(subject1, subject2, object1, object2) jika kita memperkenalkan sintaks SVO.
Ini adalah MIMO jika dipahami sebagai pipeline, seperti yang dicatat oleh @oxinabox .

Bagaimana jika menggunakan (x)f sebagai f(x) ?
(x)f(y) dapat dibaca sebagai f(x)(y) dan f(y)(x) jadi pilih evaluasi dengan benar terlebih dahulu:

(x)f # f(x)
(x)f(y) # f(y)(x)
(x)f(y)(z) # f(y)(z)(x)
(x)(y)f(z) # f(z)(y)(x)
(a)(b)f(c)(d) # f(c)(d)(b)(a)
1(2(3, 4), 5(6, 7), 8(9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10))
1 <| (2 <| (3, 4), 5 <| (6, 7), 8 <| (9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10)), but 2 <| (3, 4) == 2((3, 4)) so currently emit error
3 |> 2(_, 4) |> 1(_, 5(6, 7), 8(9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10))
((3)2(_, 4))1(_, 5(6, 7), 8(9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10))
(3, 4) |> 2(_, _) |> 1(_, 5(6, 7), 8(9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10))
((3, 4)2)1(_, 5(6, 7), 8(9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10))

Ini dapat memanipulasi vararg dengan jelas.
Tetapi itu akan merusak operator biner tanpa spasi:

(a + b)+(c + d) # +(c + d)(a + b) == (c + d)(a + b): Error

Opsi alternatif: tambahkan kasus lain untuk sintaks percikan. Memiliki f ... (x) desugar ke (args ...) -> f (x, args ...)

Ini akan memungkinkan pembuatan kari yang ringan secara sintaksis (manual):

#Basic example:
f(a,b,c,d) = #some definition
f...(a)(b,c,d) == f(a,b,c,d)
f...(a,b)(c,d) == f(a,b,c,d)
f...(a,b,c)(d) == f(a,b,c,d)
f...(a)...(b)(c,d) == f(a,b,c,d) # etc etc

# Use in pipelining:
x |> map...(f) |> g  |> filter...(h) |> sum

Kapan Anda berhenti memasak kari? Fungsi Julia tidak memiliki aritas tetap.

Julia sudah memiliki operator percikan. Apa yang saya usulkan akan memiliki perilaku yang persis sama dengan operator percikan saat ini.

I, e: f ... (x) == (args ...) -> f (x, args ...) adalah gula untuk membuat lambda dengan percikan.

Definisi itu selalu memberi Anda objek fungsi. Agaknya Anda terkadang menginginkan jawaban.

Anda mendapatkan itu dengan secara eksplisit memanggil objek di akhir. Sebagai contoh, perhatikan kekurangan ... sebelum tanda kurung terakhir dalam contoh terakhir saya f ... (a) ... (b) (c, d) == f (a, b, c, d) .

Anda juga bisa memanggil objek fungsi yang dikembalikan dengan |>, yang membuatnya bagus untuk pemipaan.

@solusiholcim

Contoh dasar:

f (a, b, c, d) = # definisi beberapa
f ... (a) (b, c, d) == f (a, b, c, d)
f ... (a, b) (c, d) == f (a, b, c, d)
f ... (a, b, c) (d) == f (a, b, c, d)
f ... (a) ... (b) (c, d) == f (a, b, c, d) # dll dll

Intuisi yang baik untuk menggunakan percikan dengan rangkaian fungsi tetapi menurut saya terlalu rumit.
Anda membuat banyak aplikasi pada satu titik rantai.
Dalam rangkaian fungsi, Anda membuat satu aplikasi selangkah demi selangkah di sepanjang rantai.

Dan @StefanKarpinski benar, Anda tidak tahu kapan harus berhenti menerapkan fungsi pada dirinya sendiri dan akhirnya menerapkannya ke item yang lebih skalar.

- (terpotong) -

Maaf, itulah yang agak tidak berguna dan tidak terbaca.
Lihat pesan kedua saya di bawah ini untuk mendapatkan penjelasan yang lebih jelas (saya harap).

Mengingat betapa fungsionalnya Julia, saya sangat menyukai ide @saolof tentang operator fungsi-kari. Saya tidak begitu mengerti dari mana asal keberatan semantik, karena sepertinya ini memiliki interpretasi yang sangat jelas.

Anda bahkan dapat membuat prototipe dari kenyamanan repl Anda sendiri:

ctranspose(f) = (a...) -> (b...) -> f(a..., b...)

map'(+)(1:10)

map'(+)'(1:10, 11:20)(21:30)

(+)'(1,2,3)(4,5)

1:10 |> map'(x->x^2) |> filter'(iseven)

Menurutku ada perasaan yang menyenangkan.

Sunting: Terasa seperti ini juga bisa menjadi jalan untuk menggeneralisasi ini lebih banyak. Jika kita dapat menulis map∘(+, 1:10) maka kita dapat menulis map∘(_, 1:10) untuk menempatkan argumen kari terlebih dahulu, dan operator kari menentukan ruang lingkup lambda, memecahkan masalah terbesar untuk kari umum semacam itu.

Eh, itu pintar, @MikeInnes.

Saya suka bagaimana ekstensibilitas ekstrim Julia terlihat di sini juga. Solusi pemersatu untuk berbagai persyaratan yang sangat luas untuk rangkaian fungsi ternyata menyalahgunakan ctranspose ...

(klarifikasi: Saya mendapatkan 1:10 |> map'(x->x^2) |> filter'(iseven) dengan proposal ini, jadi saya 💯% untuk itu!)

Untuk lebih jelasnya, saya tidak berpikir kita harus benar-benar menyalahgunakan operator adjoint untuk ini, tetapi ini adalah bukti konsep yang baik bahwa kita dapat memiliki notasi kari fungsi yang ringkas.

Mungkin kita harus memperkenalkan operator unicode baru? http://www.fileformat.info/info/unicode/char/1f35b/index.htm

(Maaf...)

Saya merasa _'s masih merupakan cara yang jauh lebih fleksibel untuk membuat lambda

@bramtayl Saya pikir ide dalam suntingan MikeInnes untuk postingnya adalah bahwa keduanya dapat hidup berdampingan - garis bawah mandiri seperti dalam permintaan tarik @stevengj akan berfungsi, kari mandiri seperti dalam ide Mike di atas akan berhasil, dan menggabungkan keduanya juga akan berfungsi, memungkinkan Anda menggunakan operator kari untuk membatasi cakupan _ s di dalamnya.

ah mengerti

Itu membuatnya tidak terlalu berbeda dengan LazyCall.jl

Atau proposalnya di sini: https://github.com/JuliaLang/julia/pull/24990#issuecomment -350490856

Pada catatan yang lebih serius:

Untuk lebih jelasnya, saya rasa kita tidak seharusnya menyalahgunakan operator adjoint untuk ini

Mungkin pilihan yang tepat. Namun, saya ingin menyuarakan harapan saya, jika solusi seperti itu diterapkan, diberikan operator yang mudah mengetik. Kemampuan untuk melakukan sesuatu seperti 1:10 |> map'(x->x^2) secara signifikan kurang berguna jika karakter apa pun yang menggantikan ' mengharuskan saya untuk mencarinya di tabel unicode (atau menggunakan editor yang mendukung LaTeX-expansions).

Daripada menyalahgunakan operator adjoint, kami dapat menggunakan kembali splat.

  • dalam konteks perpipaan (linier)
  • di dalam, dalam panggilan fungsi

    • lakukan percikan sebelum dan bukan setelah

begitu

  • percikan dapat menyebabkan arg iterator hilang

Semacam gambar percikan tingkat tinggi, (dengan anacrusis jika ada musisi di sana).
Berharap itu tidak mengguncang terlalu banyak bahasa.

CONTOH

1:10
    |> map(...x->x^2)
    |> filter(...iseven)

CONTOH 2

genpie = (r, a=2pi, n=12) ->
  (0:n-1) |>
      map(...i -> a*i/n) |>
      map(...t -> [r*cos(t), r*sin(t)]) 

bisa berdiri untuk

elmap = f -> (s -> map(f,s))

genpie = (r, a=2pi, n=12) ->
  (0:n-1) |>
      elmap(i -> a*i/n) |>
      elmap(t -> [r*cos(t), r*sin(t)]) 

Tidak yakin apakah ini termasuk di sini, karena diskusi telah berkembang menjadi rantai dan sintaks yang lebih maju / fleksibel ... tetapi kembali ke posting pembuka, rantai fungsi dengan sintaks titik tampaknya mungkin sekarang, dengan sedikit pengaturan tambahan. Sintaks ini hanyalah konsekuensi dari memiliki sintaks titik untuk struct bersama dengan fungsi / penutupan kelas satu.

mutable struct T
    move
    scale
    display
    x
    y
end

function move(x,y)
    t.x=x
    t.y=y
    return t
end
function scale(c)
    t.x*=c
    t.y*=c
    return t
end
function display()
    @printf("(%f,%f)\n",t.x,t.y)
end

function newT(x,y)
    T(move,scale,display,x,y)
end


julia> t=newT(0,0)
T(move, scale, display, 0, 0)

julia> t.move(1,2).scale(3).display()
(3.000000,6.000000)

Sintaksnya tampaknya sangat mirip dengan OOP konvensional, dengan kekhasan "metode kelas" yang bisa berubah. Tidak yakin apa implikasi kinerjanya.

@ivanctong Apa yang Anda jelaskan adalah sesuatu yang lebih mirip dengan antarmuka yang lancar daripada rangkaian fungsi.

Meskipun demikian, menyelesaikan masalah rangkaian fungsi secara lebih umum akan memiliki manfaat tambahan karena juga dapat digunakan untuk antarmuka yang lancar. Meskipun saat ini pasti mungkin untuk membuat sesuatu seperti antarmuka yang lancar menggunakan anggota struct di Julia, menurut saya sangat bertentangan dengan semangat dan estetika desain Julia.

Cara elixir melakukannya di mana operator pipa selalu lewat di sisi kiri sebagai argumen pertama dan memungkinkan argumen tambahan sesudahnya, sangat berguna, saya ingin melihat sesuatu seperti "elixir" |> String.ends_with?("ixir") sebagai warga kelas satu di Julia.

Bahasa lain mendefinisikannya sebagai Sintaks Panggilan Fungsi Seragam .
Fitur ini menawarkan beberapa keunggulan (lihat Wikipedia), alangkah baiknya jika Julia mendukungnya.

Jadi, apakah ada antarmuka yang fasih ke Julia saat ini?

Silakan posting pertanyaan ke forum diskusi wacana Julia .

Dalam situasi peretasan (dan penilaian yang dipertanyakan !?) Saya telah membuat solusi lain yang mungkin untuk ketatnya pengikatan placeholder fungsi:

https://github.com/c42f/MagicUnderscores.jl

Seperti dicatat di https://github.com/JuliaLang/julia/pull/24990 , ini didasarkan pada pengamatan bahwa seseorang sering menginginkan slot tertentu dari fungsi tertentu untuk mengikat ekspresi placeholder _ erat, dan orang lain secara longgar. MagicUnderscores membuat ini dapat dikembangkan untuk semua fungsi yang ditentukan pengguna (sangat sesuai dengan semangat mesin siaran). Dengan demikian kita dapat memiliki hal-hal seperti

julia> <strong i="12">@_</strong> [1,2,3,4] |> filter(_>2, _)
2-element Array{Int64,1}:
 3
 4

julia> <strong i="13">@_</strong> [1,2,3,4] |> filter(_>2, _) |> length
2

"bekerja saja". (Dengan @_ jelas akan hilang jika memungkinkan untuk menjadikan ini solusi umum.)

Beberapa variasi saran @MikeInnes akan tampak memadai untuk kebutuhan saya (biasanya rantai panjang filter, map, kurangi, enumerasi, zip dll. Menggunakan sintaks do ).

c(f) = (a...) -> (b...) -> f(a..., b...)

1:10 |> c(map)() do x
    x^2
end |> c(filter)() do x
    x > 50
end

Ini berfungsi, meskipun saya tidak bisa mendapatkan ' untuk bekerja lagi. Ini sedikit lebih pendek dari:

1:10 |> x -> map(x) do x
    x^2
end |> x -> filter(x) do x
    x > 50
end

Juga saya rasa orang bisa melakukannya

cmap = c(map)
cfilter = c(filter)
cetc = c(etc)
...

1:10 |> cmap() do x
    x^2
end |> cfilter() do x
    x > 50
end |> cetc() do ...

Mulai 1.0 Anda harus membebani adjoint alih-alih ctranspose . Anda juga bisa melakukan:

julia> Base.getindex(f::Function, x...) = (y...) -> f(x..., y...)

julia> 1:10 |> map[x -> x^2] |> filter[x -> x>50]
3-element Array{Int64,1}:
  64
  81
 100

Jika kita bisa membebani apply_type maka kita bisa mendapatkan map{x -> x^2} :)

@MikeInnes Saya baru saja mencuri itu

Kontribusi yang terlambat dan sedikit sembrono - bagaimana menyalurkan data ke lokasi mana pun dalam daftar argumen menggunakan kombinasi operator kari kiri dan kanan:

VERSION==v"0.6.2"
import Base: ctranspose, transpose  
ctranspose(f::Function) = (a...) -> ((b...) -> f(a..., b...))  
 transpose(f::Function) = (a...) -> ((b...) -> f(b..., a...))

"little" |> (*)'''("Mary ")("had ")("a ") |> (*).'(" lamb")

Clojure memiliki beberapa makro threading yang bagus. Apakah kita memilikinya di ekosistem Julia di suatu tempat?

Clojure memiliki beberapa makro threading yang bagus. Apakah kita memilikinya di ekosistem Julia di suatu tempat?

https://github.com/MikeInnes/Lazy.jl

Clojure memiliki beberapa makro threading yang bagus. Apakah kita memilikinya di ekosistem Julia di suatu tempat?

kami memiliki setidaknya 10 dari mereka.
Saya memposting daftar lebih jauh di utas.
https://github.com/JuliaLang/julia/issues/5571#issuecomment -205754539

Dapatkah Anda mengedit daftar agar memiliki LightQuery alih-alih dua paket saya yang lain?

Karena operator |> berasal dari elixir mengapa tidak mengambil inspirasi dari salah satu cara mereka membuat fungsi anonim?
dalam elixir Anda dapat menggunakan &expr untuk menentukan fungsi anonim baru dan &n untuk menangkap argumen posisi ( &1 adalah argumen pertama, &2 adalah yang kedua, dll.)
Di elixir ada hal tambahan untuk ditulis, (misalnya Anda memerlukan titik sebelum tanda kurung untuk memanggil fungsi anonim &(&1 + 1).(10) )

Tapi di julia akan terlihat seperti ini

&(&1 * 10)        # same as: v -> v * 10
&(&2 + 2*&5)      # same as: (_, x, _, _, y) -> x + 2*y
&map(sqrt, &1)    # same as: v -> map(sqtr, v)

Jadi kita bisa menggunakan operator |> dengan lebih baik

1:9 |> &map(&1) do x
  x^2
end |> &filter(&1) do x
  x in 25:50
end

dari pada

1:9 |> v -> map(v) do x
  x^2
end |> v -> filter(v) do x
  x in 25:50
end

perhatikan bahwa Anda dapat mengganti baris 2 dan 3 dengan .|> &(&1^2) atau .|> (v -> v^2)

Perbedaan utama dengan proposisi dengan _ placeholder adalah bahwa di sini dimungkinkan untuk menggunakan argumen posisi, dan & di depan ekspresi membuat ruang lingkup placeholder jelas (bagi pembaca dan kompiler).

Perhatikan bahwa saya telah mengambil & dalam contoh saya, tetapi penggunaan ? , _ , $ atau yang lainnya, tidak akan mengubah apa pun ke kasus.

Scala menggunakan _ untuk argumen pertama, _ untuk argumen kedua, dll., Yang ringkas, tetapi Anda akan segera kehabisan situasi di mana Anda dapat menerapkannya (tidak dapat mengulang atau membalik urutan argumen). Ia juga tidak memiliki awalan ( & dalam saran di atas) yang menghilangkan fungsi dari ekspresi, dan itu, dalam praktiknya, adalah masalah lain yang mencegah penggunaannya. Sebagai seorang praktisi, Anda akhirnya membungkus fungsi inline yang dimaksudkan dalam tanda kurung ekstra dan tanda kurung kurawal, dengan harapan akan dikenali.

Jadi saya akan mengatakan bahwa prioritas tertinggi ketika memperkenalkan sintaks seperti ini adalah tidak ambigu.

Tetapi untuk prefiks untuk argumen, $ memiliki tradisi di dunia skrip shell. Itu selalu bagus untuk menggunakan karakter yang sudah dikenal. Jika |> berasal dari Elixir, maka itu bisa menjadi argumen untuk mengambil & dari Elixir, juga, dengan gagasan bahwa pengguna sudah berpikir dalam mode itu. (Dengan asumsi ada banyak mantan pengguna Elixir di luar sana ...)

Satu hal yang sintaksis seperti ini mungkin tidak pernah bisa tangkap adalah membuat fungsi yang mengambil argumen N tetapi menggunakan lebih sedikit dari N. $1 , $2 , $3 dalam body menyiratkan adanya 3 argumen, tetapi jika Anda ingin meletakkan ini pada posisi di mana ia akan dipanggil dengan 4 argumen (yang terakhir diabaikan), maka tidak ada cara alami untuk mengungkapkannya. (Selain mendefinisikan fungsi identitas untuk setiap N dan membungkus ekspresi dengan salah satunya.) Ini tidak relevan untuk kasus motivasi meletakkannya setelah |> , yang hanya memiliki satu argumen.

Saya memperpanjang trik @MikeInnes untuk membebani getindex, menggunakan Colon seolah-olah fungsinya adalah array:

struct LazyCall{F} <: Function
    func::F
    args::Tuple
    kw::Dict
end

Base.getindex(f::Function,args...;kw...) = LazyCall{typeof(f)}(f,args,kw)

function (lf::LazyCall)(vals...; kwvals...)

    # keywords are free
    kw = merge(lf.kw, kwvals)

    # indices of free variables
    x_ = findall(x->isa(x,Colon),lf.args)
    # indices of fixed variables
    x! = setdiff(1:length(lf.args),x_)

    # the calling order is aligned with the empty spots
    xs = vcat(zip(x_,vals)...,zip(x!,lf.args[x!])...)
    args = map(x->x[2],sort(xs;by=x->x[1]))

    # unused vals go to the end
    callit = lf.func(args...,vals[length(x_)+1:end]...; kw...)

    return callit
end

[1,2,3,4,1,1,5]|> replace![ : , 1=>10, 3=>300, count=2]|> filter[>(50)]  # == [300]

log[2](2) == log[:,2](2) == log[2][2]() == log[2,2]()  # == true

Ini jauh lebih lambat daripada lambda atau makro threading, tapi menurut saya sangat keren: p

Untuk mengingatkan orang-orang yang berkomentar di sini, lihatlah diskusi yang relevan di https://github.com/JuliaLang/julia/pull/24990.

Selain itu, saya akan mendorong Anda untuk mencoba https://github.com/c42f/Underscores.jl yang memberikan implementasi yang ramah fungsi dari sintaks _ untuk placeholder. @jpivarski berdasarkan contoh Anda, Anda mungkin merasa cukup familiar dan nyaman.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

TotalVerb picture TotalVerb  ·  3Komentar

musm picture musm  ·  3Komentar

StefanKarpinski picture StefanKarpinski  ·  3Komentar

StefanKarpinski picture StefanKarpinski  ·  3Komentar

tkoolen picture tkoolen  ·  3Komentar