Mengesampingkan WB (yang sebenarnya tidak diperlukan untuk menjawab pertanyaan Anda) - masalahnya tampaknya memiliki jawaban langsung hanya berdasarkan bagaimana ekspresi dievaluasi selama penugasan. Ini contohnya:
In[1505]:=
notGoodQ[x_]:=True;
Clear[g];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
In[1509]:= g/:cccQ[g[x0_]]:=True
During evaluation of In[1509]:= g::nogood: -- Message text not found -- (x0_)
Out[1509]= $Aborted
Untuk membuatnya bekerja, saya sengaja membuat definisi untuk notGoodQ
untuk selalu mengembalikan True
. Sekarang, mengapa g[x0_]
dievaluasi selama penugasan melalui TagSetDelayed
? Jawabannya adalah, saat TagSetDelayed
(serta SetDelayed
) dalam tugas h/:f[h[elem1,...,elemn]]:=...
tidak menerapkan aturan apa pun yang f
mungkin, itu akan mengevaluasi h[elem1,...,elem2]
, serta f
. Ini contohnya:
In[1513]:=
ClearAll[h,f];
h[___]:=Print["Evaluated"];
In[1515]:= h/:f[h[1,2]]:=3
During evaluation of In[1515]:= Evaluated
During evaluation of In[1515]:= TagSetDelayed::tagnf: Tag h not found in f[Null]. >>
Out[1515]= $Failed
Fakta bahwa TagSetDelayed
adalah HoldAll
tidak berarti bahwa itu tidak mengevaluasi argumennya - itu hanya berarti bahwa argumen yang sampai padanya tidak dievaluasi, dan apakah mereka akan dievaluasi atau tidak tergantung pada semantik TagSetDelayed
(yang saya jelaskan secara singkat di atas). Hal yang sama berlaku untuk SetDelayed
, jadi pernyataan yang umum digunakan bahwa "tidak mengevaluasi argumennya" tidak benar secara harfiah. Pernyataan yang lebih benar adalah bahwa ia menerima argumen yang tidak dievaluasi dan mengevaluasinya dengan cara khusus - tidak mengevaluasi r.h.s, sedangkan untuk l.h.s., mengevaluasi kepala dan elemen tetapi tidak menerapkan aturan untuk kepala. Untuk menghindarinya, Anda dapat membungkus sesuatu dalam HoldPattern
, seperti ini:
Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;
Ini melewati. Berikut beberapa kegunaannya:
In[1527]:= cccQ[g[1]]
Out[1527]= True
In[1528]:= cccQ[g[2]]
During evaluation of In[1528]:= g::nogood: -- Message text not found -- (2)
Out[1528]= $Aborted
Namun perhatikan bahwa kebutuhan untuk HoldPattern
di dalam sisi kiri Anda saat membuat definisi sering kali merupakan tanda bahwa ekspresi di dalam kepala Anda juga dapat dievaluasi selama pemanggilan fungsi, yang dapat merusak kode Anda. Berikut adalah contoh dari apa yang saya maksud:
In[1532]:=
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
Kode ini mencoba menangkap kasus seperti h[f[something]]
, tapi itu jelas akan gagal karena f[something]
akan mengevaluasi sebelum evaluasi datang ke h
:
In[1535]:= h[f[5]]
Out[1535]= h[25]
Bagi saya, kebutuhan untuk HoldPattern
di l.h.s. adalah tanda bahwa saya perlu mempertimbangkan kembali desain saya.
EDIT
Mengenai debugging saat memuat di WB, satu hal yang dapat Anda lakukan (IIRC, tidak dapat memeriksa sekarang) adalah menggunakan pernyataan cetak lama yang baik, yang outputnya akan muncul di konsol WB. Secara pribadi, saya jarang merasa membutuhkan debugger untuk tujuan ini (paket debug saat memuat)
EDIT 2
Menanggapi suntingan dalam pertanyaan:
Mengenai urutan definisi:ya, Anda dapat melakukan ini, dan ini memecahkan masalah khusus ini. Tetapi, secara umum, ini tidak kuat, dan saya tidak akan menganggapnya sebagai metode umum yang baik. Sulit untuk memberikan saran yang pasti untuk kasus yang dihadapi, karena ini sedikit keluar dari konteksnya, tetapi menurut saya penggunaan UpValues
di sini tidak dibenarkan. Jika ini dilakukan untuk penanganan kesalahan, ada cara lain
untuk melakukannya tanpa menggunakan UpValues
.
Umumnya, UpValues
paling sering digunakan untuk membebani beberapa fungsi dengan cara yang aman, tanpa menambahkan aturan apa pun ke fungsi yang kelebihan beban. Satu saran adalah untuk menghindari mengasosiasikan UpValues
dengan kepala yang juga memiliki DownValues
dan mungkin mengevaluasi -dengan melakukan ini Anda mulai bermain game dengan evaluator, dan akhirnya akan kalah. Yang paling aman adalah melampirkan UpValues
ke simbol inert (kepala, wadah), yang sering kali mewakili "tipe" objek yang ingin Anda beri beban berlebih pada fungsi tertentu.
Mengenai komentar saya tentang keberadaan HoldPattern
menunjukkan desain yang buruk. Pasti ada ada penggunaan yang sah untuk HoldPattern
, seperti ini (agak artifisial):
In[25]:=
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]
Out[27]= {a,b,c}
Di sini dibenarkan karena dalam banyak kasus Plus
tetap tidak dievaluasi, dan berguna dalam bentuknya yang tidak dievaluasi - karena seseorang dapat menyimpulkan bahwa itu mewakili jumlah. Kami membutuhkan HoldPattern
di sini karena cara Plus
didefinisikan pada argumen tunggal, dan karena suatu pola kebetulan merupakan argumen tunggal (walaupun umumnya menggambarkan banyak argumen) selama definisi. Jadi, kami menggunakan HoldPattern
di sini untuk mencegah memperlakukan pola sebagai argumen normal, tetapi ini sebagian besar berbeda dari kasus penggunaan yang dimaksudkan untuk Plus
. Kapan pun ini masalahnya (kami yakin bahwa definisi akan berfungsi dengan baik untuk kasus penggunaan yang dimaksudkan), HoldPattern
baik-baik saja. Perhatikan b.t.w., bahwa contoh ini juga rapuh:
In[28]:= ff[Plus[a]]
Out[28]= ff[a]
Alasan mengapa sebagian besar masih OK adalah karena biasanya kami tidak menggunakan Plus
pada satu argumen.
Namun, ada kelompok kasus kedua, di mana struktur argumen yang biasanya diberikan sama dengan struktur pola yang digunakan untuk definisi. Dalam hal ini, evaluasi pola selama penugasan menunjukkan bahwa evaluasi yang sama akan terjadi dengan argumen aktual selama pemanggilan fungsi. Penggunaan Anda termasuk dalam kategori ini. Komentar saya untuk cacat desain adalah untuk kasus-kasus seperti itu - Anda dapat mencegah pola dari evaluasi, tetapi Anda harus mencegah argumen dari evaluasi juga, untuk membuat ini berfungsi. Dan pencocokan pola terhadap ekspresi yang tidak sepenuhnya dievaluasi adalah rapuh. Juga, fungsi tidak boleh mengasumsikan beberapa kondisi tambahan (di luar apa yang dapat diketik-periksa) untuk argumen.