Parantezleri, noktaları, kaşlı ayraçları, = (işlevler) vb. Atlayabileceğiniz kesin kurallar nelerdir?


106

Parantezleri, noktaları, kaşlı ayraçları, = (işlevleri) vb. Atlayabileceğiniz (atlayabileceğiniz) kesin kurallar nelerdir?

Örneğin,

(service.findAllPresentations.get.first.votes.size) must be equalTo(2).
  • service benim nesnem
  • def findAllPresentations: Option[List[Presentation]]
  • votes İadeler List[Vote]
  • gerekir ve olmalıdır her ikisi de özelliklerin işlevleri

Neden gidemiyorum:

(service findAllPresentations get first votes size) must be equalTo(2)

?

Derleyici hatası:

"RestServicesSpecTest.this.service.findAllPresentations türü Seçenek [List [com.sharca.Presentation]] parametreleri almaz"

Neden bir parametreyi geçirmeye çalıştığımı düşünüyor? Neden her yöntem çağrısı için nokta kullanmalıyım?

Neden (service.findAllPresentations get first votes size)eşit olmalıdır (2) sonuç:

"bulunamadı: önce değer"

Yine de, "2'ye eşit olmalıdır", (service.findAllPresentations.get.first.votes.size)2'ye eşit olmalıdır, yani yöntem zincirleme iyi çalışıyor mu? - nesne zinciri zinciri zinciri parametresi.

Scala kitabına ve web sitesine baktım ve kapsamlı bir açıklama bulamıyorum.

Aslında, Rob H'nin Stack Overflow sorusunda açıkladığı gibi Scala'da hangi karakterleri atlayabilirim? , '.' işaretini atlamak için tek geçerli kullanım durumu bu. "işlenen işleç işlenen" tarzı işlemler içindir, yöntem zincirleme için değil?

Yanıtlar:


87

Yanıtı bulmuş gibisin. Neyse, netleştirmeye çalışacağım.

Operatör gösterimi olarak adlandırılan önek, ek ve sonek gösterimleri kullanırken noktayı atlayabilirsiniz . Operatör notasyonunu kullanırken ve ancak o zaman, yönteme ikiden az parametre geçirilmişse parantezi atlayabilirsiniz.

Şimdi, operatör gösterimi yöntem çağrısı için bir gösterimdir , yani çağrılan nesnenin yokluğunda kullanılamaz.

Gösterimleri kısaca detaylandıracağım.

Önek:

Sadece ~, !, +ve -önek notasyonu kullanılabilir. Bu, yazarken !flagveya yazarken kullandığınız gösterimdir val liability = -debt.

Infix:

Yöntemin bir nesne ve parametreleri arasında göründüğü gösterim budur. Aritmetik operatörlerin hepsi buraya uyar.

Sonek (ayrıca son ek):

Bu gösterim, yöntem bir nesneyi takip ettiğinde ve hiçbir parametre almadığında kullanılır . Örneğin, yazabilirsiniz list tailve bu sonek gösterimidir.

Hiçbir yöntem curried olmadığı sürece, infix gösterimi çağrılarını sorunsuz bir şekilde zincirleyebilirsiniz. Örneğin, şu stili kullanmayı seviyorum:

(list
 filter (...)
 map (...)
 mkString ", "
)

Şununla aynı şey:

list filter (...) map (...) mkString ", "

Şimdi, filtre ve harita tek bir parametre alıyorsa neden burada parantez kullanıyorum? Çünkü onlara anonim işlevler aktarıyorum. Anonim fonksiyon tanımlarını infix stiliyle karıştıramıyorum çünkü anonim fonksiyonumun sonu için bir sınıra ihtiyacım var. Ayrıca, anonim işlevin parametre tanımı, infix yönteminin son parametresi olarak yorumlanabilir.

İnfix'i birden çok parametre ile kullanabilirsiniz:

string substring (start, end) map (_ toInt) mkString ("<", ", ", ">")

Curried işlevlerin infix gösterimi ile kullanılması zordur. Katlama işlevleri bunun açık bir örneğidir:

(0 /: list) ((cnt, string) => cnt + string.size)
(list foldLeft 0) ((cnt, string) => cnt + string.size)

İnfix çağrısının dışında parantez kullanmanız gerekir. Burada oyunun kurallarından tam olarak emin değilim.

Şimdi postfix hakkında konuşalım. Postfix'in kullanımı zor olabilir çünkü bir ifadenin sonu dışında hiçbir yerde kullanılamaz . Örneğin, aşağıdakileri yapamazsınız:

 list tail map (...)

Çünkü ifadenin sonunda kuyruk görünmez. Bunu da yapamazsınız:

 list tail length

İfadelerin sonunu işaretlemek için parantez kullanarak ek gösterimini kullanabilirsiniz:

 (list tail) map (...)
 (list tail) length

Güvenli olmayabileceği için sonek gösteriminin tavsiye edilmediğini unutmayın .

Umarım bu tüm şüpheleri ortadan kaldırmıştır. Değilse, sadece bir yorum bırakın ve onu geliştirmek için ne yapabileceğime bakayım.


ahh, yani benim ifademde şunu söylüyorsunuz: (((((realService findAllPresentations) get) vot) size) 2'ye eşit olmalıdır - get, first, vot ve size parametrelerin hiçbirini almadıkları için sonek operatörleri ? bu yüzden ne olması gerektiğini, ne olması gerektiğini ve eşit olduğunu merak ediyorum ...
Antony Stubbs

Böyle bir şey söylemiyorum, yine de durumun bu olduğundan oldukça eminim. :-) "be" sözdizimini daha güzel hale getirmek için muhtemelen yardımcı bir nesnedir. Veya, daha spesifik olarak, infix gösteriminin "zorunluluk" ile kullanılmasını sağlamak için.
Daniel C. Sobral

Peki get Option.get, İlk olarak list.first, oylar bir case sınıfı özelliği ve boyut list.size. Şimdi ne düşünüyorsun?
Antony Stubbs

Ah evet - bu, "(realService findPresentation 1) .get.id'nin 1'e eşit olması gerektiği" gerçeğiyle pekiştirilmiştir - Service # findPresentations (id: Int) göründüğü gibi bir infix operatörüdür. Harika - Sanırım şimdi anladım. :)
Antony Stubbs

42

Sınıf tanımları:

valveya varparametreyi özel yapacak sınıf parametrelerinden çıkarılabilir.

Var veya val eklenmesi, bunun halka açık olmasına neden olur (yani, yöntem erişimcileri ve mutatörler oluşturulur).

{} sınıfın gövdesi yoksa atlanabilir, yani

class EmptyClass

Sınıf somutlaştırması:

Derleyici tarafından çıkarılabiliyorsa, genel parametreler ihmal edilebilir. Ancak, türleriniz eşleşmiyorsa, tür parametresi her zaman eşleşecek şekilde çıkarılır. Dolayısıyla, türü belirtmeden beklediğinizi alamayabilirsiniz - yani verilen

class D[T](val x:T, val y:T);

Bu size bir tür hatası verecektir (Int bulundu, beklenen String)

var zz = new D[String]("Hi1", 1) // type error

Oysa bu iyi çalışıyor:

var z = new D("Hi1", 1)
== D{def x: Any; def y: Any}

Çünkü tür parametresi, T, ikisinin en az yaygın olan süper türü olarak çıkarılır - Herhangi biri.


Fonksiyon tanımları:

= işlev Birim (hiçbir şey) döndürürse bırakılabilir.

{}işlev gövdesi için işlev tek bir deyimse bırakılabilir, ancak yalnızca ifade bir değer döndürürse ( =işarete ihtiyacınız vardır ), yani,

def returnAString = "Hi!"

ama bu çalışmıyor:

def returnAString "Hi!" // Compile error - '=' expected but string literal found."

İşlevin dönüş türü, çıkarım yapılabiliyorsa ihmal edilebilir (özyinelemeli bir yöntemin dönüş türü belirtilmelidir).

() işlev herhangi bir argüman almazsa bırakılabilir, yani,

def endOfString {
  return "myDog".substring(2,1)
}

hangi geleneksel olarak yan etkisi olmayan yöntemler için ayrılmıştır - daha sonra bunun üzerine.

()isme göre geçiş parametresi tanımlanırken aslında kendiliğinden düşülmez, ancak aslında anlamsal olarak oldukça farklı bir gösterimdir, yani,

def myOp(passByNameString: => String)

MyOp'un isme göre geçirilen bir parametre aldığını ve bunun da işlev parametrelerinin aksine bir String (yani bir dize döndüren bir kod bloğu olabilir) ile sonuçlandığını söylüyor.

def myOp(functionParam: () => String)

hangi diyor myOphiç parametreye sahiptir ve bir String döndüren bir işlev alır.

(Unutmayın, adıyla geçirilen parametreler işlevler halinde derlenir; yalnızca sözdizimini daha güzel kılar.)

() İşlev yalnızca bir bağımsız değişken alırsa işlev parametresi tanımına bırakılabilir, örneğin:

def myOp2(passByNameString:(Int) => String) { .. } // - You can drop the ()
def myOp2(passByNameString:Int => String) { .. }

Ancak birden fazla argüman gerektiriyorsa, () şunu eklemelisiniz:

def myOp2(passByNameString:(Int, String) => String) { .. }

Beyanlar:

.yalnızca infix operatörleri için kullanılabilen operatör gösterimini kullanmak için bırakılabilir (argüman alan yöntem operatörleri). Daha fazla bilgi için Daniel'in cevabına bakın .

  • . sonek işlevleri listesi kuyruğu için de bırakılabilir

  • () sonek operatörleri list.tail için bırakılabilir

  • () şu şekilde tanımlanan yöntemlerle kullanılamaz:

    def aMethod = "hi!" // Missing () on method definition
    aMethod // Works
    aMethod() // Compile error when calling method

Çünkü bu gösterim, List # tail gibi yan etkisi olmayan yöntemler için ayrılmıştır (yani, yan etkisi olmayan bir işlevin çağrılması, işlevin dönüş değeri dışında hiçbir gözlemlenebilir etkisinin olmadığı anlamına gelir).

  • () tek bir argümandan geçerken operatör notasyonu için bırakılabilir

  • () bir ifadenin sonunda olmayan sonek operatörlerini kullanmak gerekebilir

  • () iç içe geçmiş ifadeleri, anonim işlevlerin sonlarını veya birden fazla parametre alan operatörler için gerekli olabilir

Bir işlevi alan bir işlevi çağırırken, () işlevini iç işlev tanımından çıkaramazsınız, örneğin:

def myOp3(paramFunc0:() => String) {
    println(paramFunc0)
}
myOp3(() => "myop3") // Works
myOp3(=> "myop3") // Doesn't work

İsme göre parametre alan bir işlevi çağırırken, bağımsız değişkeni parametresiz anonim bir işlev olarak belirtemezsiniz. Örneğin, verilen:

def myOp2(passByNameString:Int => String) {
  println(passByNameString)
}

Bunu şu şekilde adlandırmalısınız:

myOp("myop3")

veya

myOp({
  val source = sourceProvider.source
  val p = myObject.findNameFromSource(source)
  p
})

Ama değil:

myOp(() => "myop3") // Doesn't work

IMO, bırakılan dönüş türlerinin aşırı kullanımı kodun yeniden kullanılması için zararlı olabilir. Koddaki açık bilgi eksikliğinden dolayı okunabilirliğin azalmasına iyi bir örnek için spesifikasyona bakın. Bir değişkenin türünün gerçekte ne olduğunu bulmak için yapılan dolaylı seviyelerin sayısı fındık olabilir. Umarım daha iyi araçlar bu sorunu önleyebilir ve kodumuzu kısa tutabilir.

(Tamam, daha eksiksiz, kısa bir cevap derleme arayışında (eğer bir şeyi kaçırdıysam veya yanlış / yanlış bir şey aldıysam lütfen yorum yapın), cevabın başına ekledim. Lütfen bunun bir dil olmadığını unutmayın şartname, bu yüzden akademik olarak tam olarak doğru yapmaya çalışmıyorum - daha çok bir referans kartı gibi.)


10
Ağlıyorum. Bu nedir.
Profpatsch

12

Çeşitli koşullar hakkında fikir veren bir alıntılar koleksiyonu ...

Şahsen, şartnamede daha fazlası olacağını düşündüm. Eminim olmalı, sadece doğru kelimeleri aramıyorum ...

Bununla birlikte, birkaç kaynak var ve bunları bir araya topladım, ancak yukarıdaki sorunları bana açıklayan gerçekten eksiksiz / kapsamlı / anlaşılır hiçbir şey yok ...:

"Bir yöntem gövdesinde birden fazla ifade varsa, onu kaşlı ayraçlar {…} ile çevrelemelisiniz. Yöntem gövdesinde tek bir ifade varsa küme ayraçlarını atlayabilirsiniz."

Gönderen bölüm 2'deki, "Tür Az, daha fazlası" Scala Programlama :

"Üst yöntemin gövdesi, eşittir işaretinden sonra gelir '='. Neden eşittir işareti? Neden Java'da olduğu gibi sadece kaşlı ayraçlar {…} olmasın? Çünkü noktalı virgüller, işlev dönüş türleri, yöntem bağımsız değişkenleri listeleri ve hatta küme parantezleri Eşittir işaretinin kullanılması, bazı olası ayrıştırma belirsizliklerini önler.Bir eşittir işaretinin kullanılması, bize fonksiyonların bile Scala'daki değerler olduğunu hatırlatır; Scala. "

Gönderen arasında: 1. bölümde, "Scala Tanıtımı Sıfır Altmış için" Scala Programlama :

"Parametresi olmayan bir işlev, parantez olmadan bildirilebilir, bu durumda parantez olmadan çağrılmalıdır. Bu, arayan kişinin sembolün bir değişken mi yoksa bir işlev mi yoksa bir değişken mi olduğunu bilmemesi için Tekdüzen Erişim İlkesi için destek sağlar. parametreleri.

Bir değer döndürüyorsa (yani dönüş türü Birim dışında bir şeyse) işlev gövdesinden önce "=" gelir, ancak tür Birim olduğunda dönüş türü ve "=" göz ardı edilebilir (yani bir yordam gibi görünür) bir işlevin aksine).

Gövde etrafındaki parantez gerekli değildir (gövde tek bir ifadeyse); daha kesin olarak, bir işlevin gövdesi yalnızca bir ifadedir ve birden çok parçadan oluşan herhangi bir ifade parantez içine alınmalıdır (tek parçalı bir ifade isteğe bağlı olarak parantez içine alınabilir). "

"Sıfır veya bir bağımsız değişkenli işlevler nokta ve parantez olmadan çağrılabilir. Ancak herhangi bir ifadenin etrafında parantez olabilir, böylece noktayı çıkarabilir ve yine de parantez kullanabilirsiniz.

Ve parantez kullanabildiğiniz her yerde parantez kullanabildiğiniz için, noktayı atlayabilir ve birden çok ifade içerebilen parantez içine alabilirsiniz.

Bağımsız değişken içermeyen işlevler parantezler olmadan çağrılabilir. Örneğin, String üzerindeki length () işlevi "abc" .length () yerine "abc" .length olarak çağrılabilir. İşlev, parantez olmadan tanımlanan bir Scala işleviyse, işlev parantez olmadan çağrılmalıdır.

Geleneksel olarak, println gibi yan etkileri olan hiçbir argümana sahip olmayan işlevler parantez içinde çağrılır; yan etkisi olmayanlar parantezsiz olarak adlandırılır. "

Scala Syntax Primer blog gönderisinden :

"Prosedür tanımı, sonuç türünün ve eşittir işaretinin çıkarıldığı bir işlev tanımıdır; tanımlayıcı ifadesi bir blok olmalıdır. Örneğin, def f (ps) {stats}, def f (ps) ile eşdeğerdir: Birim = {istatistik }.

Örnek 4.6.3 İşte write adlı bir prosedürün bir bildirimi ve tanımı:

trait Writer {
    def write(str: String)
}
object Terminal extends Writer {
    def write(str: String) { System.out.println(str) }
}

Yukarıdaki kod örtük olarak aşağıdaki kodla tamamlanmıştır:

trait Writer {
    def write(str: String): Unit
}
object Terminal extends Writer {
    def write(str: String): Unit = { System.out.println(str) }
}"

Dil spesifikasyonundan:

"Yalnızca tek bir parametre alan yöntemlerle Scala, geliştiricinin. Karakterini bir boşlukla değiştirmesine ve parantezleri çıkarmasına izin vererek ekleme operatörü örneğimizde gösterilen operatör sözdizimini etkinleştirir. Bu sözdizimi, Scala API'sinin diğer yerlerinde kullanılır. Range örnekleri oluştururken:

val firstTen:Range = 0 to 9

Burada da yine (Int), bir sınıf içinde bildirilen bir vanilya yöntemidir (aslında burada daha fazla örtük tür dönüştürmesi vardır, ancak kaymayı elde edersiniz). "

Gönderen Java Mülteciler Bölüm 6 için Scala: Üzeri Java Alma :

"Şimdi," m 0 "ı denediğinizde, Scala geçerli bir nesne olmadığı gerekçesiyle (~,!, - ve +) bunu tekli bir operatör olarak atar." M "nin geçerli bir nesne olduğunu bulur - bu bir yöntem değil, bir işlevdir ve tüm işlevler nesnedir.

"0" geçerli bir Scala tanımlayıcısı olmadığından, ne bir infix ne de bir sonek operatörü olamaz. Bu nedenle, Scala beklediğinden şikayet ediyor ";" - bu iki (neredeyse) geçerli ifadeyi ayırır: "m" ve "0". Eğer eklediyseniz, m'nin ya bir argüman gerektirdiğinden ya da başarısız olursa, onu kısmen uygulanan bir işleve dönüştürmek için bir "_" olduğundan şikayet ederdi. "

"İşleç sözdizimi stilinin yalnızca sol tarafta açık bir nesne olduğunda işe yaradığına inanıyorum. Sözdiziminin amacı," işlenen işleç işlenen "stil işlemlerini doğal bir şekilde ifade etmenize olanak sağlamaktır."

Scala'da hangi karakterleri atlayabilirim?

Ama aynı zamanda kafamı karıştıran şu alıntı:

"Bir yöntem çağrısını almak için bir nesne olması gerekir. Örneğin, println bir nesne alıcısına ihtiyaç duyduğundan" println "Merhaba Dünya!" "Yapamazsınız. İhtiyacı karşılayan "Konsol yazdırma" "Merhaba Dünya!" "Yapabilirsiniz."

Bildiğim kadarıyla gördüğünüz gibi çünkü orada olduğunu çağrıyı almak için bir nesne ...


1
Tamam, bazı ipuçları ve woah almak için Specs kaynağını okumayı denedim. Bu, sihirli kodla ilgili sorunların harika bir örneğidir - çok fazla karışım, tür çıkarımı ve örtük dönüştürme ve örtük parametreler. Dışarıdan içeriden anlamak çok zor! Bunun gibi büyük kütüphaneler için, daha iyi araçlar bazı harikalar yaratabilir ... bir gün ...
Antony Stubbs

3

Bu genel kurala uymayı daha kolay buluyorum: ifadelerde yöntemler ve parametreler arasında boşluklar yer alıyor. Örnekte, (service.findAllPresentations.get.first.votes.size) must be equalTo(2)olarak ayrıştırır (service.findAllPresentations.get.first.votes.size).must(be)(equalTo(2)). 2'nin etrafındaki parantezlerin boşluklardan daha yüksek bir ilişkilendirilebilirliğe sahip olduğuna dikkat edin. Noktalar ayrıca daha yüksek bir ilişkiye sahiptir, bu nedenle (service.findAllPresentations.get.first.votes.size) must be.equalTo(2)olarak ayrıştırılır (service.findAllPresentations.get.first.votes.size).must(be.equalTo(2)).

service findAllPresentations get first votes size must be equalTo 2olarak ayrıştırır service.findAllPresentations(get).first(votes).size(must).be(equalTo).2.


2

Aslında, ikinci okumada, belki de anahtar budur:

Yalnızca tek bir parametre alan yöntemlerle Scala, geliştiricinin. boşluk bırakın ve parantezleri atlayın

Blog gönderisinde belirtildiği gibi: http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6 .

Yani belki de bu aslında çok katı bir "sözdizimi şekeri" dir ve yalnızca bir parametreyi alan bir nesnede bir yöntemi etkin bir şekilde çağırdığınız yerde çalışır . Örneğin

1 + 2
1.+(2)

Ve başka hiçbir şey.

Bu, sorudaki örneklerimi açıklar.

Ama dediğim gibi, eğer biri dil spesifikasyonunda bunun tam olarak nerede belirtildiğini gösterebilirse, çok memnun olur.

Tamam, bazı iyi adamlar (#scala'dan paulp_) bu bilginin dil spesifikasyonunda nerede olduğuna işaret etti:

6.12.3: Operatörlerin önceliği ve ilişkilendirilebilirliği, bir ifadenin parçalarının gruplandırılmasını aşağıdaki gibi belirler.

  • Bir ifadede birden fazla infix işlemi varsa, daha yüksek önceliğe sahip operatörler, daha düşük önceliğe sahip operatörlerden daha yakından bağlanır.
  • Ardışık ek işlemleri varsa e0 op1 e1 op2. . .opn tr operatörleri op1,. . . , aynı önceliğe sahipse, tüm bu operatörler aynı çağrışım özelliğine sahip olmalıdır. Tüm operatörler sola ilişkilendirilebilirse, dizi (... [E0 op1 e1) op2..) Opn en. Aksi takdirde, tüm operatörler doğru ilişkiliyse, dizi e0 op1 (e1 op2 (. .Opn en)..) Olarak yorumlanır.
  • Sonek operatörleri her zaman infix operatörlerinden daha düşük önceliğe sahiptir. Örneğin, e1 op1 e2 op2 her zaman (e1 op1 e2) op2'ye eşdeğerdir.

Bir sol-ilişkisel operatörün sağ-el operandı, parantez içine alınmış çeşitli argümanlardan oluşabilir, örneğin e op (e1,.., En). Bu ifade daha sonra e.op (e1,..., En) olarak yorumlanır.

Bir sol ilişkisel ikili işlem e1 op e2, e1.op (e2) olarak yorumlanır. Op sağ ilişkisel ise, aynı işlem {val x = e1; e2.op (x)}, burada x yeni bir addır.

Hmm - bana göre gördüklerimle uyuşmuyor ya da anlamıyorum;)


hmm, karışıklığa daha fazla eklemek için, bu aynı zamanda geçerlidir: (((((realService findAllPresentations) get) ilk) oy) boyut) 2'ye eşit olmalı, ancak bu parantez çiftlerinden herhangi birini kaldırırsam değil ...
Antony Stubbs

2

Hiç yok. Fonksiyonun yan etkilerinin olup olmadığı konusunda muhtemelen tavsiye alacaksınız. Bu sahte. Düzeltme, Scala'nın izin verdiği makul ölçüde yan etkileri kullanmamaktır. Yapamayacağı ölçüde, tüm bahisler kapalıdır. Tüm bahisler. Parantez kullanmak "tümü" kümesinin bir öğesidir ve gereksizdir. Tüm bahisler kapandığında herhangi bir değer sağlamaz.

Bu tavsiye, esasen başarısız olan bir efekt sistemine yönelik bir girişimdir (şununla karıştırılmamalıdır: diğer efekt sistemlerinden daha az faydalıdır).

Yan etki yapmamaya çalışın. Bundan sonra, tüm bahislerin kapalı olduğunu kabul edin. Bir efekt sistemi için fiili sözdizimsel gösterimin arkasına saklanmak yalnızca zarar verebilir ve yapar.


Peki, karma bir OO / Fonksiyonel dil ile çalışırken sorun bu değil mi? Herhangi bir pratik örnekte, yan etki fonksiyonlarına sahip olmak isteyeceksiniz ... Bize "etki sistemleri" hakkında biraz bilgi verebilir misiniz? Sanırım daha fazla alıntı yapmak gerekirse, "Parametre içermeyen bir işlev parantez olmadan bildirilebilir, bu durumda parantez olmadan çağrılmalıdır Bu, Tekdüzen Erişim İlkesi için destek sağlar, böylece arayan kişi, simgesi bir değişken veya parametresi olmayan bir işlevdir. "
Antony Stubbs
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.