è³æ: http://www.scala-lang.org/docu/files/ScalaByExample.pdf
5. First-Class Functions
é¢æ°ããã¡ã¼ã¹ãã¯ã©ã¹ãªè©±ã
5.1 Anonymous Functions
ãã§ã«åºã¦ããã¨æããã©
(x: Int) => x * x
ãå¿åé¢æ°ãInt åã®å¼æ°ã 1 ã¤åãåã£ã¦ãäºä¹ãè¿ãããã ããInt => Int ã§ãããã¨ãæèãããããå ´åã¯
x => x * x
ã ãæ¸ãã¦ããããã¤ã³ã¿ããªã¿ã§ããã ã試ãã¨ãã¨ãã¯åãå¿ è¦ãªã®ã§ãå¤å° lightweight ã§ãªãæ°åã«ãªã£ããã¾ãã¨ã¦ãäºç´°ãªè©±ã ãã©ã
以ä¸ã® 2 ã¤ãåå¤ã
(x1: T1, ..., xn: Tn) => E { def f(x1: T1, ..., xn: Tn) = E; f _ } // ãã ã f 㯠fresh
ãªã®ã§ Scala ã§ã¯å¿åé¢æ°ã¯ syntax sugar ãããããããã£ãããããããããã¾ã§ã« _ ã®èª¬æããªãã£ããã
5.2. Currying
ã«ãªã¼åã®è©±ãæ³å®èªè
ã Java ããã°ã©ãã®ããã ãããã©ã説æãåé·ã
ææ³ã®è©±ã®è¦ç´ã ãã以ä¸ã® 2 ã¤ãåå¤ã
def f (x1: T1) ... (xn: Tn) = E def f = (x1: T1) => ... => (xn: Tn) => E
5.3. Example: Finding Fixed Ponts of Functions, 5.4. Summary
ã¹ã«ã¼
5.5. Language Elements Seen Sor Far
ããã¾ã§ã®ææ³ã® BNF çãªãã®ã
- ãªãã©ã«ã¯ Java ã¨åãã
- èå¥åã¯ä»¥ä¸ã®ãããªæãã«æ¸ãã¦ããã
letter ::= 'a' ... 'z' operator ::= '#', '+', ':', etc. ident ::= letter (letter | digit)* | operator+ | ident '_' ident
ããããã®éãã ã¨ãx_ ã _x ã f_1 ãèå¥åã«ãªããªããã§ã試ãã¦ã¿ãã¨èå¥åã«ãªã£ã¦ããææ³ã®ééãããæªå®ç¾©æåãã
æ®éã®è¨èªã¨éã£ã¦ã以ä¸ã®ãããªã®ãèå¥åã
+ -- foldl_: +_vector
ããã¼ãDSL ä½ãç²æã®ãããããªè¨èªã§ããã
ãã¨ã¯ç´°ãããã¨ãx+-y 㯠x ã+- ãy ã«ãªãã®ã§ãx+(-y) ã¨ããæå³ã§æ¸ãããã£ããã¹ãã¼ã¹ãããã$ ã¯ã³ã³ãã¤ã©ç¨ã«äºç´ããã¦ããã®ã§ä½¿ããªã
èå¥å以å¤ã®ç¥è¦ã¯ä»¥ä¸ã®ã¨ããããã ã説æç¨ã®ä¸æ£ç¢ºãª BNF ã¿ãããªã®ã§ããã¾ãå½ã¦ã«ãªããªãã
- => Unit ã¨ããåã¯ãªããdef foo(f: => Unit) ã® => ã¯åã§ã¯ãªã call-by-name ã®æ示ã«ãããªãã¿ããã
- åé æ¼ç®å㯠+ 㨠- 㨠! 㨠~ ã ãã£ã½ããåå®ç¾©ãåºæ¥ããã©ãããªã©ã¯ããããªãã
6. Classes and Objects
ããã¾ãé¢ç½ããªãã®ã§ã¹ã«ã¼æ°å³ã«ã
ç´°ãããã¨
- class ã®ä¸ã§ private def x = e ã private val x = e ã¨ããã° private ã¡ã³ãã«ãªããã
- ç¶æ¿å ãçç¥ããå ´å㯠scala.AnyRef ãç¶æ¿ãããã¨ã«ãªãããJava å®è£ ã®å ´åããã㯠java.lang.Object ã® alias ã
- ãªã¼ãã¼ã©ã¤ãããé㯠override def ã¨æ示çã«æ¸ãã¦ãã
- 0 å¼æ°ã¡ã½ããã¨ç¡å¼æ°ã¡ã½ããã®éããdef 㨠val ã®éãã¯ãã§ã«èª¬æããã£ãã¨ããã
- abstract class ã¯ãåç¥ã®ã¨ããã
- traits ã¯ãã§ã«åºã¦ããã¨ããã
object å®ç¾©
Hello, world ã«ã§ã¦ãããsingleton ãªãã¸ã§ã¯ãã¿ãããªãç¹ç°ã¡ã½ããã®ãããªãã¸ã§ã¯ãã¿ãããªãã¤ã
class å®ç¾©ã¨ã»ã¼åãææ³ã§ object å®ç¾©ãã§ããããã ãã³ã³ã¹ãã©ã¯ã¿ã®ä»®å¼æ°ã¯ãªããobject å®ç¾©ã¯ã©ãã«ã§ããããããããå¿
è¦ã«ãªãã¾ã§ãªãã¸ã§ã¯ãçæã¯å»¶æãããã
companion object ã®èª¬æã¯ãªãã£ãã
æ¨æºã¯ã©ã¹
Int ã Boolean ã®æ¨æºã¯ã©ã¹ã¯ 32 bit æ´æ°ãªã Java ã® boolean ãªãã«æé©åããããã©ãè¨èªä»æ§ä¸ããã¯ãã ã®ãªãã¸ã§ã¯ãã«ããè¦ããªãããå½ããåã ããã
ãã¨é
延è©ä¾¡ããããã Boolean ã ifThenElse ãèªåã§å®ç¾©ã§ãããã ãããããããã
ãã¨ãã£ããã ãã Church encoding ã«ãã Int ã®å®è£
ãæ¸ãã¦ã¿ããã
ã¨ããå
容ã ã£ãã
7. Case Classes and Pattern Matching
è¨ç®æ©ãé¡æã«ã
æ¼ç®å¼ã表ãæ¨ãèãããæ´æ°ããªã¼ãã+ ããã¼ããããããªãã¸ã§ã¯ãæåã§æ®éã«è¡¨ç¾ããã«ã¯ãæ´æ°ã¨ãã¼ãããããã Number 㨠Sum ã¿ãããªã¯ã©ã¹ã¨ãã¦å®ç¾©ããããã®æ¼ç®å¼ãè©ä¾¡ããã«ã¯ãNumber 㨠Sum ã« eval ã¡ã½ãããããããæãããããã§ããã
ããã§çµããã°ããããã©ããã¯ãããªãããã®æ¼ç®å¼ã«ãeval ã«å ãã¦æ°ããªæä½ (print) ãå®è£
ããå ´åãèããã¨ãNumber 㨠Sum ã®ä¸¡æ¹ã®ã¯ã©ã¹ã« print ã¡ã½ãããæããããã¨ã«ãªããæ¢åã®ã¯ã©ã¹ãåå©ç¨ã§ãã¦ãªããã ããããã
ã¨ããã®ã¯ぼくも前から言っていた不満ã
ããã§ãªã¼ãã³ã¯ã©ã¹ãã§ã¯ãªããã¢ã¹ãã¯ãæåããã°ã©ãã³ã°ãã§ããªããã±ã¼ã¹ã¯ã©ã¹ã¨ãã¿ã¼ã³ãããã³ã°ã
7.1. Case Classes and Case Objects
ãã¥ã¼ããªã¢ã«ã¨ãã¶ããã©ã
abstract class Expr case class Number(n: Int) extends Expr case class Sum(e1: Expr, e2: Expr) extends Expr
æ®éã®ã¯ã©ã¹ã¨ã®éãã
- ã³ã³ã¹ãã©ã¯ã¿é¢æ°ãåæã«å®ç¾©ããããã®ã§ãnew Number(1) ã Number(1) ã¨æ¸ããã
- toString ãequals ãhashCode ã¡ã½ãããåæã«é©å½ã«å®ç¾©ãããã
- ç¡å¼æ°ã®ã¢ã¯ã»ãµãå®ç¾©ããããNumber(1).n ã¨ãã
- ãã¿ã¼ã³ãããã«ãããããã
7.2. Pattern Matching
ãããªããmatch 㯠Scala ã®ã«ã¼ãã¯ã©ã¹ Any ã«å®ç¾©ãããã¡ã½ããã ãã£ã¦æ¸ãã¦ãããæ¬å½ï¼
1 match { case 1 => 1 } // åã 1.match({ case 1 => 1 }) // error: identifier expected but 'match' found.
ãªãã ãã©ãªããã«ã¼ãã¯ã©ã¹ã¯ Any ãããªã㦠AnyRef ãªæ°ãããããæã®è©±ã¨ãããªã
ãã¿ã¼ã³ã¨ãã¦æ¸ããã®ã¯ä»¥ä¸ã®éãã
- ã±ã¼ã¹ã¯ã©ã¹ã®ã³ã³ã¹ãã©ã¯ã¿
- ãã¿ã¼ã³å¤æ° (å°æåã§å§ã¾ã)
- ã¯ã¤ã«ãã«ã¼ããã¿ã¼ã³ _
- ãªãã©ã«
- å®æ°èå¥å (大æåã§å§ã¾ã)
Scala ã¯ãã¼ã«ã«å¤æ°ã大æåå°æåã©ã¡ãããå§ãã¦ããããã©ããã¿ã¼ã³å¤æ°ã¯å°æåã§ãªãã¨ãã¡ã
{ case P1 => E1 ... case Pn => En }
ã¯å¼æ°ã 1 ååãåã£ã¦ãã¿ã¼ã³ãããããå¿åé¢æ°ãã¤ã¾ã以ä¸ã¨åãã
(x => x match { case P1 => E1 ... case Pn => En })
8. Generic Types and Methods
ã¹ã¿ãã¯ã®ãããªãã¼ã¿æ§é ããè¦ç´ ã®åãã¨ã«å®ç¾©ããã®ã¯ããã ãããããã§ã¸ã§ããªã¯ã¹ãã¨ããããããå°å ¥ã
abstract class Stack[A] { def push(x: A): Stack[A] = new NonEmptyStack[A](x, this) def isEmpty: Boolean def top: A def pop: Stack[A] } class EmptyStack[A] extends Stack[A] { def isEmpty = true def top = error("EmptyStack.top") def pop = error("EmptyStack.pop") } class NonEmptyStack[A](elem: A, rest: Stack[A]) extends Stack[A] { def isEmpty = false def top = elem def pop = rest }
Stack ã®ä¸ã§ NonEmptyStack ãåç
§ãã¦ãã¦ãNonEmptyStack 㯠Stack ãç¶æ¿ãã¦ãããã対話çç°å¢ã«æµããããªãããã¡ã¤ã«ã«ãã¦ã³ã³ãã¤ã«ãããããªãï¼
åããã©ã¡ã¼ã¿ã¨ãã¦åãåããåå¤ç¸ãªã¡ã½ããã
def isPrefix[A](p: Stack[A], s: Stack[A]): Boolean = {
p.isEmpty ||
p.top == s.top && isPrefix[A](p.pop, s.pop)
}
é¢ç½ãã¨ãããªã®ã«ãã³ã³ãã¤ã«æéãé
ãããããã§ã¿ãã¿ãããæ°ã失ã£ã¦ããï¼
å¤ç¸ã®è±åèªã® polymorphic ã¯ãã®ãªã·ã£èªç±æ¥ã§ having many forms ã¨ããæå³ããããç¥ãããã£ãã
åå¤ç¸ã®ã¡ã½ãããå¼ã¶å ´åã¯åãå¼æ°ã¨ãã¦æ¸¡ãã
val s1 = new EmptyStack[String].push("abc") val s2 = new EmptyStack[String].push("abx").push(s1.top) println(isPrefix[String](s1, s2))
ãã ããåæ¨è«ã§ããå ´åã¯åã®å¼æ°ãçç¥ã§ããã
println(isPrefix(s1, s2))
試ãã« new EmptyStack ã¨ã ããã¦ã¿ãããEmptyStack[Nothing] åã«ãªã£ãã
object EmptyStack[A] extends Stack[A] 㯠syntax error ãåãã©ã¡ã¼ã¿ã¨ã¯ããå¼æ°ãåãããããªã
8.1. Type Parameter Bounds
éåãç¾ãã¯ã©ã¹ãèãããè¦ç´ ã®åã¯éå®ããããªãã®ã§ãã©ã¡ã¼ã¿åããã
abstract class Set[A] { def incl(x: A): Set[A] def contains(x: A): Boolean }
ã§ãããã®å®è£
ãäºåæ¢ç´¢æ¨ã§ä¸ãããã¨æã£ãããA ã®ã¤ã³ã¹ã¿ã³ã¹ã¨ A ã®ã¤ã³ã¹ã¿ã³ã¹ã®å¤§å°ãæ¯è¼ã§ããªãã¦å°ãã
ããããã¨ãã¯ãåãã©ã¡ã¼ã¿ã« upper bound ãè¨å®ããã
trait Ordered[A] { // æ£ãªã this ã大ãããè² ãªã that ã大ããã0 ãªãçãã def cmp(that: A): Int } class Set[A <: Ordered[A]] { def incl(x: A): Set[A] def contains(x: A): Boolean } class EmptySet[A <: Ordered[A]] extends Set[A] { def incl(x: A) = null // å®è£ çç¥ def contains(x: A) = false }
A <: Ordered[A] ã¨ãããã¨ã§ãA 㯠Ordered[A] ã®ãµãã¯ã©ã¹ã«å¶éãããã
ã§ãè¦ç´ ã®ã¯ã©ã¹ã¯ Ordered[èªå] ãç¶æ¿ãã¦ããã
case class Num(value: Double) extends Ordered[Num] { def cmp(that: Num): Int = if (this.value < that.value) -1 else if (this.value > that.value) 1 else 0 }
ãã㨠EmptySet ã®è¦ç´ ã«ã§ããã
val s = new EmptySet[Num].incl(Num(1.0)).incl(Num(2.0)) // 以ä¸ã¯ java.io.File 㯠Ordered[java.io.File] ã®ãµãã¯ã©ã¹ãããªããããã¡ val s = new EmptySet[java.io.File]
ã¨ã¯ãããè¦ç´ ã¨ããåãå¿
ã Ordered[èªå] ãç¶æ¿ãã¦ãªãã¨ãããªãã®ã¯ä¸ä¾¿ã§ãããããããã¨ãã¯ãA <: Ordered[A] ã®ä»£ããã« A <% Ordered[A] ã使ãã¨ãããããããã®å ´åããA ã Ordered[A] ã®ãµãã¯ã©ã¹ãã¨ããå¶éããããA ãã Ordered[A] ã¸ã®æé»çãªå¤æ (implicit conversion) ãå®ç¾©ããã¦ãããã¨ããå¶éã«ç·©åããããããªãimplicit conversion ã¯å¾ã§åºã¦ããã¨ã®ãã¨ã
8.2. Variance Annotations
co-variant 㨠contra-variant ã®è©±ã
Stack[String] 㯠Stack[AnyRef] ã®ãµãã¿ã¤ãã¨ã¿ãªãã¦ããããStack ããã®ç« (8 ç« ) ã®æåã§æ¸ãããããªå®è£
(mutable cell ãªã©æããªã pure functional ãªå®è£
) ã«ãªã£ã¦ããªãåé¡ãªããã¤ã¾ã Stack 㯠co-variant ã
ã§ã Scala ã¯ããã©ã«ã㧠non-variant ãæ¡ç¨ãããã¤ã¾ããStack[String] 㨠Stack[AnyRef] ã¯ãµãã¯ã©ã¹ã§ãã¹ã¼ãã¼ã¯ã©ã¹ã§ããªãã以ä¸ã®ãããªä»£å
¥ãããã¨ã³ã³ãã¤ã«æã«ä¾å¤ã«ãªããå®éã«ã¯åé¡ãªãã«ããããããã
val string_stack = new EmptySet[String] val anyref_stack : Stack[AnyRef] = string_stack // åã¨ã©ã¼
ãããéãããã«ããã«ã¯ãStack ã®åãã©ã¡ã¼ã¿ã« + ãã¤ããããã㧠Stack ã co-variant ã¨ãªãã
class Stack[+A] { def push(x: A): Stack[A] = ... }
ã£ã¦ãco-variant ã«ãããã¾ããå ´åã¯èªå·±è²¬ä»»ã§ããï¼ã¨ä¸ç¬é ãããã£ããã©ãã¡ããã¨åãã§ãã¯ãã¦ããããã¨ããããcontra-variant ã®ä½ç½®ã« A ãåºã¦ãã¾ã£ã¦ãã (push(x: A) ã®ã¨ãã) ã®ã§ãããã ãã ã¨åã¨ã©ã¼ã«ãªã£ã¦ãã¾ãããããä½ã¨ãããã«ã¯æ¬¡ç¯ã® lower bound ã使ãã¨ã®ãã¨ã
ã¡ãªã¿ã«ãcontra-variant ã«ãããå ´å㯠- ãã¤ããããã¨ãArray ã co-variant ã«ãªã£ã¦ããã¨ãããJava ä»æ§çå®ã®æåãªçæ¨ãã¹ãç´¹ä»ããã¦ãã (æå
çããåçè«ãåå¼·ãã¦ããªãè
ãè¨èªè¨è¨ããããããããããã¹ãèµ·ãããã¨è¨ã£ã¦ã) ã
8.3. Lower Bounds
ãã£ããåçè«ãå¿ãã¦ããã®ã§ãããªããããµãã§ããã
ã¨ããããããã£ãã®åé¡ã¯ä»¥ä¸ã®ããã«ããã°éããããã
class Stack[+A] { def push[B >: A](x: B): Stack[B] = ... }
B >: A ã¯ããB 㯠A ã®ã¹ã¼ãã¼ã¿ã¤ããã¨ããå¶éã
ãªãããã§åæ¨è«ãéãããcontra-variant ã®ä½ç½®ã§ãã push ã®å¼æ°ã®åã B ã«ãªã£ããå¤ããã« lower bound ã®æ示ã®ã¨ãã (B >: A) ã«ã§ã¦ãããã©ããã㯠co-variant ã®ä½ç½®ãããããã£ã¦ãcontra-variant ã®ä½ç½®ã« A ãç¾ããªãã®ã§ OK ã
ã©ãç解ãã¹ãããStack[String] ã« AnyRef ã®ã¤ã³ã¹ã¿ã³ã¹ã push ããã¨èããã¨ãA = String ãB = AnyRef ã«ãªã£ã¦ãå¤ããå¤ã¯ Stack[String] ã§ãªã Stack[AnyRef] ãã¤ã¾ã Stack[B] ãå®éã«ãã£ã¦ãã§ããã
çã«ã¤ã¾ã¾ããæ°åã§ãããã¾ããã©ãã co-variant ã®ä½ç½®ã§ã©ãã contra-variant ã®ä½ç½®ãã¯ãã¡ããã¨èããã°ããããã ããããã¶ãã
ã¡ãªã¿ã«ãT >: S <: U ã§ãT 㯠S ã®ã¹ã¼ãã¼ã¿ã¤ããã㤠U ã®ãµãã¿ã¤ããã¨ãããã¨ãæ¸ãããããã
8.4. Least Types
å¤ç¸åã®ã¹ã¿ãã¯ã§ object EmptyStack ãä½ã話ããã¼ã¨ãHaskell ã§ãã㨠[] : [a] ãocaml ã§ãã㨠[] :: 'a list ã§ãããã
co-variant ãªã¹ã¿ãã¯ãªã以ä¸ã®ã¤ãã£ãªã ã§å®ç¾©ã§ããã
object EmptyStack extends Stack[Nothing] { ... }
ããNothing ã ã
Nothing ã¯ããã åã§ãNothing åã«ãªãå¤ã¯åå¨ããªããã¤ã¾ã Stack[Nothing] ã¯è¦ç´ ã«ãªãããå¤ãåå¨ããªãã®ã§ãå¿
ç¶çã« EmptyStack ã¨ãªãã
Nothing åã¯ä»»æã®åã®ãµãã¿ã¤ãããªã®ã§ãStack ã co-variant ã¨ãã¦å®ç¾©ããã¦ãããã¨ãããä»»æã® T ã«å¯¾ã㦠Stack[Nothing] 㯠Stack[T] ã®ãµãã¿ã¤ãã
co-variant ã®ã¹ã¿ãã¯ã¯å°ããåãã大ããåã¸åºãã¦ããã (Stack[String] ãã String[AnyRef]) ã®ã§ãåæ§ã«ãä»»æã®å T ã«å¯¾ã㦠Stack[Nothing] ãã Stack[T] ã¸åºããããã
以ä¸ããã
val s = EmptyStack.push("abc").push(new AnyRef())
ã¨æ¸ãããEmptyStack ã ããªã Stack[Nothing] åãString ã push ãã㨠Stack[String] åãAnyRef ã push ãã㨠Stack[AnyRef] åã
ãã¼ãTAPL ã«ãããªè©±ãã£ãæ°ããããã
ä»ã¾ã§ã®ã³ã¼ããã¾ã¨ããã¨ãããªãã
abstract class Stack[+A] { def push[B >: A](x: B): Stack[B] = new NonEmptyStack(x, this) def isEmpty: Boolean def top: A def pop: Stack[A] } object EmptyStack extends Stack[Nothing] { def isEmpty = true def top = error("EmptyStack.top") def pop = error("EmptyStack.pop") } class NonEmptyStack[+A](elem: A, rest: Stack[A]) extends Stack[A] { def isEmpty = false def top = elem def pop = rest }
ãã¼ãããããããã©ä½¿ãããªããããããããã¨ã¯ããããã¼ã¿æ§é ãªãã¦æ»
å¤ã«èªåã§å®ç¾©ããªãã®ã§ããããã使ããã¨ããã¾ããªãããã
ãã¦ãScala ã©ã¤ãã©ãªã®å¤ãã®ã¯ã©ã¹ã¯ã¸ã§ããªãã¯ã«æ¸ããã¦ããã以ä¸ããã®ä»£è¡¨ã® tuple ã¯ã©ã¹ã¨é¢æ°ã¯ã©ã¹ãåãä¸ããã
8.5. Tuples
å®ç¾©ã
case class Tuple2[A, B](_1: A, _2: B)
ä½ãæ¹ã
val p = new Tuple2[Int, Int](1, 2) val p = Tuple2(1, 2) val p = (1, 2) // syntax sugar
åç §ã®ä»æ¹ã
println(p._1) p match { case Tuple2(a, b) => println(a) } p match { case (a, b) => println(a) } // syntax sugar
Tuple ããªãè¨èªã§ã¯çãã¦ããã¾ããã
8.6. Functions
String => Int ãªé¢æ°ã¯ãFunction1[String, Int] ã®ã¤ã³ã¹ã¿ã³ã¹ãFunction1 ã¯ä»¥ä¸ã®ããã«å®ç¾©ããã¦ããã
trait Function1[-A, +B] { def apply(x: A): B }
åç主義çã§ãããä¸å¿ç¢ºããã¦ã¿ãã
((x:Int) => x * x).apply(10) // 100
f(x) 㯠f.apply(x) ã®ç¥è¨ã
ã¡ãªã¿ã« (T1, ..., Tn) => S 㯠Functionn[T1, ..., Tn, S] ã®ç¥è¨ãè¤æ°ã®å¼æ°ã¯ tuple æ±ããªãããããªããã§ããã
ãã¨ã¯é¢æ°ã®å¼æ°ã contra-variant ã«ãªããã¨ã®èª¬æããã£ãã
ã¾ã¨ã
å¿åé¢æ°ãã¯ã©ã¹ããã¿ã¼ã³ããããã¸ã§ããªã¯ã¹ã«ã¤ãã¦ãã¸ã§ããªã¯ã¹ãéãã£ãã
syntax sugar ã®è£ã«é ããåç主義çãªææ³ãè¦ãã¦ãã¦ãããªãé¢ç½ãã§ãããããã§ãã¦å®è¡é度ããããªãã«éãããããã ãããããã°ã£ã¦ã¾ãããã
ã¾ããJava ãã¼ã¹ã§ãªããã°ãã£ã¨ããè¨èªã«ãªã£ãæ°ãããã
大ä½ãã㧠Scala By Example ã®ååãããããã¨ã¯ãªã¹ããMutable State ãç¡éãªã¹ããlazy value ãimplicit parameter and convension ã Hindley/Milner åæ¨è«ã並åãã©ãã©ãéããªã£ã¦ããã
ãããå¯å¤é·å¼æ°ã¨ããã¼ã¯ã¼ãå¼æ°ã¨ãã¯ç¡ãã®ããªãæãªããä¸ä¿çãªèå³ã ãã©ã