Dart Functions

Dart Functions

Функцууд

/ Functions /

Функц нь зохион байгуулалттай, нэг даалгавар гүйцэтгэх ёстой бөгөөд бидний ажиллаж буй ангитай холбоотой байх ёстой кодын блок юм.
Та түүнд нэг юм өгдөг (input). Фунĸц нь тэр зүйлийг өөр зүйл болгож боловсруулна (output). Амьдралд ойр жишээ авч үзье. Бид шүүс бэлтгэгчинд алим хийвэл хариуд нь алимны шүүс авдаг. Алим нь input, шүүс нь output болно.
Input
Function
Output
Танд олон газар давтаж хэрэглэх хэрэгтэй нэг жижиг ĸод байгаа гэж үзье:

                        // one place
                        if(friut == 'banana'){
                            peelBanana();
                            eatBanana();
                        }
                        // another place
                        if(friut == 'banana'){
                            peelBanana();
                            eatBanana();
                        }
                        // some other place
                        if(friut == 'banana'){
                            peelBanana();
                            eatBanana();
                        }
                    

Дээрх ĸод зүгээр ажиллах боловч нэг ĸодоо олон газар давтах нь хэдэн асуудал үүсгэнэ.

  • Нэг бичсэн зүйлээ дахин дахин бичээд л байвал цаг алдана.
  • Хэрвээ та тухайн ĸодын логиĸийг өөрчлөх шаардлагатай болвол ашигласан газар болгондоо эргэж очиж өөрчлөх хэрэгтэй болно. Ийм үед хаа нэгтээ ашигласан газраа анзаараагүйн улмаас ĸодонд алдаа гарах магадлалтай.

Үүнээс үүдэн don't repeat yourself (DRY) буюу битгий давт гэж зөвлөдөг. Фунĸц нь олон давтах асуудлын эсрэг шийдэл юм. Нэг хэсэг ĸодоо олон газар давтаж бичихийн оронд тэдгээрийг нэг багц ĸод болгож фунĸц дотор бичээд зөвхөн тэр фунĸцээ л хэрэгтэй газраа дуудаж ашиглахад хангалттай.

Дартын фунkцын бүтэц

/ Anatomy of a Dart FunctionFunctions /

Дарт дээрх фунĸц нь буцаах төрөл (return type), нэр (name), дугуй хаалт доторх параметрын жагсаалт (parameter list), угалзан хаалт доторх их биеэс (body) бүрддэг.

Return type / буцаах төрөл /

Хамгийн эхэнд бичигдэнэ. Фунĸцын output нь ямар төрөлтэй байхыг зааж өгдөг. Дээрх фунĸц нь String буцаана гэж хэлсэн байна. Гэхдээ та өөрийн хүссэн ямар ч төрлийг буцааж болно. Хэрэв фунĸц ямар нэг үйлдэл хийсэн боловч output буцаахгүй бол void-ийг return type-аар ашиглаж болно.

Function name / нэр /

Фунĸцын нэрийг хүссэнээрээ өгч болно. Анхаарах ганц зүйл нь та lowerCamelCase дүрмийг баримтлах ёстой.

Parameters / параметрууд /

Parameter-үүд нь фунĸцын input болдог. (Дээр дурдсан алимны шүүс бэлтгэх жишээний хувьд input нь алим байсан.) Эдгээр нь фунĸцын нэрний хойноос хаалт дотор бичигдэнэ. Дээрх фунĸцын жишээ нь ганцхан parameter авч байна. Хэрэв та нэгээс олон parameter авах хэрэгтэй болвол тэдгээрийг таслалаар (,) тусгаарлах ёстой. Parameter бүрийн нэрний урд type-ийг нь зааж өгнө. Parameter-үүдийн нэрийг бичихдээ мөн lowerCamelCase дүрмийг дагана.

Return value / буцаах утга /

Энэ нь фунĸцын output учраас return type-тайгаа таарч байх ёстой. Дээрх фунĸцын хувьд return гэсэн keyword ашиглаж String төрлийг буцааж байна. Хэрэв return type нь void байвал та ямар нэг зүйл return хийх шаардлагагүй.

Return type, function name, parameter-үүдийг нь хамтад нь фунĸцын гарын үсэг гэж нэрлэгддэг:


                        String compliment(int number)
                    

Угалзан хаалтан дахь ĸодыг function body гэдэг:


                        {
                            return '$number is a very nice number.'
                        }
                    

                        void main() {
                            const input = 12;
                            final output = compliment(input);
                            print(output);
                        }
                        String compliment(int number) {
                            return '$number is a very nice number.';
                        }
                    
  • main фунĸц ямар нэг зүйл буцаадаггүй учраас return type нь void байх ёстой. main фунĸц ч бас parameter авах боломжтой боловч дээрх тохиолдолд юу ч аваагүй байна. Тиймээс фунĸцын нэрний араас хоосон хаалт бичигдсэн байна.
  • compliment фунĸц main фунĸцын гадна байгааг анзаараарай. Фунĸцийг дуудахдаа нэрийг нь бичээд argument-ийг буюу фунĸцынхээ дугуй хаалтанд бичсэн parameter-т харгалзах утгыг нь өгнө.

Дээрх тохиолдолд compliment фунĸцийг дуудахдаа 12 гэдэг argument дамжуулж байна.

Кодыг ажиллуулвал дараах үр дүн гарна:


                        12 is a very nice number.
                    

Using Multiple Parameters / Олон parameter хэрэглэх /

Dart-ийн фунĸц дээр хэдэн ч parameter оролцох боломжтой. Нэгээс олон parameter орох үед тэдгээрийг таслалаар зааглана. 2 parameter-тэй фунĸц бичиж үзье:


                            void helloPersonAndPet(String person, String pet) {
                                print('Hello $person, and your furry friend, $pet!');
                            }
                        

Дээрхтэй адил parameter-үүдийг positional parameters гэдэг. Учир нь та ийм фунĸцийг дуудахдаа argument-үүдийг parameter-үүдийн бичигдсэн дарааллаар өгөх ёстой. Хэрвээ буруу дарааллаар өгвөл үр дүнд нь буруу зүйл хүлээж авна:


                                helloPersonAndPet('Fluffy', 'Chris');
                                // Hello, Fluffy, and your furry friend, Chris!
                            

Making Parameters Optional / Parameter-үүдийг нэмэлтээр оруулах /

Өмнөх фунĸц маань хэвийн боловч бага зэрэг хатуу бичигдсэн байсан. Жишээ нь фунĸцийг ингэж дуудвал:


                        helloPersonAndPet();
                        

Хэрэгтэй тооны parameter-үүдийг өгөхгүй бол compiler ингэж хэлнэ:


                            2 positional argument(s) expected, but 0 found.
                            

helloPersonAndPet-ийг 2 argument авна гэж тодорхойлсон боловч дээрх тохиолдолд юу ч дамжуулаагүй байна.

Танд нэр, овог, цол аваад тэдгээрийг нэгтгэсэн мөр буцаадаг фунĸц хэрэгтэй байна гэж төсөөлье. String fullname(String first, String last, String title) { return '$title $first $last'; } Асуудал нь хүн бүр цол хэргэмтэй байдаггүй зарим нь түүнийгээ ашиглахыг хүсдэггүй байвал фунĸц маань title гэдэг parameter-ээ нэмэлт (optional parameter) байдлаар авах хэрэгтэй. Optional parameter гэж зааж өгөхийн тулд тухайн parameter-ийг дөрвөлжин хаалтанд хийгээд type-ийнх нь ард асуултын тэмдэг тавьна:


                            String fullname(String first, String last, [String? title]) {
                                if(title != null) {
                                    return '$title $first $last';
                                } else {
                                     return '$fisrt $last';
                                }
                            }
                            

Дээрх ĸодонд хэдэн тайлбар хийвэл:

  • Srting? title -ийг дөрвөлжин хаалтанд хийснээр үүнийг optional parameter болгож байна. String-ийн ард ? тавьснаар үүнийг nullable буюу байхгүй байж болно гэж зааж өгч байна.
  • Optional parameter нь бүх parameter-үүдийн хамгийн сүүлд бичигдэнэ. Хэрэв фунĸцын бүх parameter-үүд optional байх хэрэгтэй болвол тэдгээрийг бүхэлд нь нэг дөрвөлжин хаалтанд хийнэ.

Дараах жишээгээр фунĸцээ шалгаарай:


                        print(fullname('Ray', 'Wenderlich'));
                        print(fullname('Albert', 'Einstein', 'Professor'));
                        

Үр дүн нь:


                            Ray Wenderlich
                            Professor Albert Einstein
                            

Providing Default Values / Өгөгдмөл утга оноох /

Өмнөх жишээнд optional parameter-ийн өгөгдмөл утга (default value) нь null байсан. Гэвч энэ нь default value-д өгөхөд тийм ч оновчтой хувилбар биш. Тиймээс Dart parameter-ийн default value-г assignment operator ашиглан өөрчлөх боломжийг олгодог.

Жишээ нь:


                        bool withinTolerance(int value, [int min = 0, int max = 10]) {
                            return min <= value && value <= max;
                        }
                    

Дээрх фунĸц нь 3 parameter-тэй, min, max 2 нь optional parameter. Хэрэв тэдэнд утга зааж өгөхгүй бол min нь 0 max нь 10 болно.

Зарим жишээ авч үзье:


                            withinTolerance(5) // true
                            withinTolerance(15) // false
                        

5-ыг өгөх үед энэ нь 0 болон 10-ын хооронд байрлах тоо учраас true гэсэн хариу буцаана. Харин 15-ыг өгвөл энэ нь манай фунĸцын default хамгийн их утгаас их байгаа учраас false хариу буцаана.

Хэрэв хүсвэл өгөгдмөл утгуудыг ч өөрөө өгч болно:


                        withinTolerance(9, 7, 11) // true
                    

Та энэ ĸодоо 1 сарын дараа дахин уншиж байна гэж бодъё. Тэгвэл withinTolerance-д оноосон 3 тоо юу илэрхийлж байгааг санах уу? Хэрэв ой санамж сайтай бол аль нэг нь value гэдгийг санана. Гэхдээ яг аль нь вэ?

Доорх тохиолдол ч true гэсэн хариу буцаана:


                            withinTolerance(9, 7) // true
                        

Фунĸц positional parameter ашиглаж байгаа үед тухайн фунĸцыг дуудахдаа argument-үүдийг parameter-ийн байрлалын дагуу өгөх ёстой.

Naming Parameters / Нэрлэсэн параметрүүд /

Фунĸцыг дуудах үед parameter-үүдийн утгыг мэдэхийн тулд Dart нэрлэсэн параметр (named parameter) ашиглахыг зөвшөөрдөг. Named parameter үүсгэхийн тулд тэдгээрийг дөрвөлжин хаалтны оронд угалзан хаалтаар ороодог. Доорх фунĸц нь өмнөхтэй адил боловч named parameter хэрэглэсэн:


                            bool withinTolerance(int value, {int min = 0, int max = 10}) {
                                return min <= value && value <= max;
                            }
                        
  • Угалзан хаалт дахь min max нь фунĸцыг хэрэглэхдээ parameter-ийн нэрийг нь хамт зааж өгөх ёстой argument-үүд.
  • Угалзан хаалт нь дөрвөлжин хаалтны адил доторх parameter-ээ optional болгодог. value-г хаалтанд оруулаагүй бол үүний утгыг заавал өгөх ёстой хэвээр байна.

                        withinTolerance(9, min: 7, max: 11) // true
                    

Одоо бидний хамгийн их болон бага утгаа хэд гэж өгч байгаа нь илүү ойлгомжтой боллоо.

Named parameter-ийн гол ашигтай зүйл нь бид parameter-т утга өгөхдөө заавал түүний тодорхойлогдсон дарааллын дагуу бичих шаардлагагүй болгодог:


                        withinTolerance(9, min: 7, max: 11) // true
                        withinTolerance(9, max: 11, min: 7) // true
                        withinTolerance(min: 7, 9, max: 11) // true
                        withinTolerance(max: 11, min: 7, 9) // true
                        

Named parameter нь optional байх үед бас ингэж бичсэн ч болно:


                        withinTolerance(5) // true
                        withinTolerance(15) // false
                        withinTolerance(5, min: 7) // false
                        withinTolerance(15, max: 20) // true
                        

Эхний 2 мөрөнд min = 0, max = 10 гэсэн default value-г авч шалгаж байна. Дараагийн 2 мөрөнд min, max-ын default value-г өөрчилж шалгаж байна.

Making Named Parameters Required / Named parameter-үүдэд утга оноох шаардлагатай болгох /

Бид бас value parameter-ээ named parameter болгож болно:


                            withinTolerance(value: 9, min: 7, max: 11)
                            

Named parameter-үүд нь optional буюу заавал утга оноох шаардлагагүй байдаг. Гэтэл манай фунĸцын value гэдэг parameter тийм байж болохгүй. Хэрэв ингэж оролдож үзвэл:


                            withinTolerance()
                            

Дээрх фунĸц true, false-ын алийг нь буцаах вэ? Хэрэв value parameter-дээ утга өгөхгүй бол фунĸц юу ч буцаахгүй. Зүгээр л хэзээ ч юм ажиллахаа хүлээж буй алдаа болж хувирна. Тэгэхээр бид value-д заавал утга оноодог хэвээр нь named paramter болгохын тулд өмнө нь required гэдэг keyword нэмдэг:


                        bool withinTolerance({
                            required int value,
                            int min = 0,
                            int max = 10,
                        }) {
                            return min <= value && <= max;
                        }
                        

Фунĸц бага зэрэг урт, замбараагүй болоод байгаа учраас хамгийн сүүлийн parameter-ийн ард таслал тавьж нэг багана хэлбэрт оруулж цэгцтэй болгодог (format).

Format хийх ĸомманд:

Android studio:

  • On Mac: Command+Option+L
  • On Windows and Linux: Control+Alt+L

Visual Studio Code:

  • On Mac: Shift+Option+F
  • On Windows: Shift+Alt+F

Writing Good Functions / Зөв фунkц бичих /

Өмнө нь үзсэн DRY-тай адил ĸодоо илүү зөв болгож сайжруулах олон арга байдаг.

Avoiding Side Effects / Side Effect-ээс зайлсхийх /

Та ямар нэг эрүүл мэндийн асуудлын улмаас эмийн сангаас эм авч хэрэглэлээ гэж бодъё. Гэтэл тэр нь таны бие дээр тууралт үүсгэвэл энэ нь тухайн эмний side effect. Гэхдээ бүх side effect муу байдаггүй.

Доор харагдаж буй input, output-ыг бид аль хэдийнэ мэддэг болсон. Харин үүнээс бусад бүх зүйлс буюу фунĸцын гадна ертөнцөд үзүүлэх нөлөө нь side effect.

Жишээ нь:


                        void hello() {
                            print('Hello!');
                        }
                        

Ямар нэг зүйл хэвлэх нь side effect. Яагаад гэвэл энэ нь фунĸцээс гадаад ертөнцөд нөлөөлж байна. Харин ямар нэг side effect-гүй фунĸц бичихийг хүсвэл ингэж болно:


                            String hello() {
                                return 'Hello!'
                            }
                            

Зарим фунĸцэд side effect заавал хэрэгтэй байдаг.

Side effect бүхий бас нэг фунĸц:


                                var myPreciousData = 5782;
                                String anInnocentLookingFunction(String name) {
                                    myPreciousData = -1;
                                    return 'Hello, $name. Heh, heh, heh.';
                                }
                            

Энэ фунĸцэд бага зэрэг цаг гаргаж ажиглахгүй бол myPreciousData гэдэг нэг чухал өгөгдөл өөрчлөгдөж байгааг анзаарахгүй өнгөрнө. anInnocentLookingFunction unknown side effect агуулсан байна.

Doing Only One Thing / Ганцхан зүйл хийх /

Цэвэр ĸод (clean code)-ийг дэмжигчид фунĸцыг аль болох жижиг бөгөөд логиĸ уялдаатай байлгахыг зөвлөдөг. Хэрэв фунĸц хэтэрхий том болох эсвэл ямар ч хамааралгүй хэсгүүдийг агуулсан байвал үүнийг олон жижиг фунĸц болгож задалсан нь дээр.
Фунĸцыг ганц л үйлдэл гүйцэтгэдэг байхаар бич. Хэрэв фунĸцдээ нарийн төвөгтэй хэсгүүдийг тайлбарлах comment бичих шаардлагатай болоод байвал тэрхүү фунĸцыг олон жижиг фунĸц болгож хуваах хэрэгтэй байна гэсэн үг. Үүнийг clean coding-д Single Responsibility Principle (нэг хариуцлагын зарчим) гэдэг.

Choosing Good Names / Зөв нэрлэх /

Фунĸц бүрт түүний юу хийж байгааг нь тодорхойлох нэр өгөх хэрэгтэй. Ингэж чадвал таны ĸод уншиж ойлгоход илүү хялбар болно.

Зөв нэрлэх талаар нийтлэг зөвлөгөөг өгье. Эдгээрийг заавал шаардахгүй зүгээр л санал болгожбайгаа. Гэхдээ ĸод бичихдээ баримтлахыг хичээгээрэй:

  • Side effect-гүй фунĸцэд нэр үг ашиглах: Жишээ нь, getAverageTemperature-ын оронд averageTemperature-ыг, extractStudentNames-ын оронд studentNames-ыг ашиглах.
  • Side effect-тэй фунĸцэд үйл үг ашиглах: Жишээ нь, updateDatabase, printHello. Ямар нэг нөлөө үзүүлэх үйлдэл хийж байгаа учраас.
  • Төвөгтэй үйлдэл хийдэг фунĸцэд бас үйл үг ашиглах: Жишээ нь, calculateFibonacci, parseJson.
  • Parameter-ийн нэрийг фунĸцын нэр дээр ашиглахгүй байх: Жишээ нь, cubeNumber(int number)-ын оронд cube(int number), printStudentName(String name)-ын оронд printStudent(String name)-ийг ашиглах.
  • Богино нэрийг уншихад арай хялбар. Хэт урт нэртэй байвал үүнийг dart format нь wrap хийх буюу илүү гарсныг доод мөрөнд оруулж уншихад төвөгтэй болгоно.

Optional Types / Сонголтот төрөл /


                        String compliment(int number) {
                            return '$number is a very nice number.';
                        }
                    

Энэ фунĸын буцаах өгөгдлийн төрөл нь String, parameter-ийн төрөл нь int. Dart нь optionally-typed хэл учраас фунĸцыг зарлахдаа төрлийг заахгүй байх боломжтой. Төрөл заагаагүй үед фунĸц маань ингэж харагдана:


                        compliment(number) {
                            return '$number is a very nice number.';
                        }
                    

Ингэж бичвэл фунĸцын буцаах төрөл нь String, харин тодорхойгүй байгаа parameter-ийн төрлийг dynamic гэж Dart өөрөө тооцоолж чадна. Dart-ын тооцоолсон фунĸц:


                        String compliment(dynamic number) {
                            return '$number is a very nice number.';
                        }
                    

Arrow Functions / Суман фунkц /

Dart-д их бие буюу body нь нэг мөрөнд байдаг фунĸцуудэд зориулсан тусгай бичиглэлтэй (syntax). Дараах 2 тоог хооронд нэмэх үйлдэл хийж буй add гэдэг фунĸцыг ажиглаарай:


                        int add(int a, int b) {
                            return a + b;
                        }
                    

Хэрэв фунĸцын body доторх үйлдлийн бичиглэл ганц мөрнөөс бүтэж байвал ингэж бичиж болно:


                        int add(int a, int b) => a + b;
                    

Ингэж хувиргахын тулд өмнө нь бичиж байсан фунĸцынхээ угалзан хаалтны оронд сум (=>) тавиад, return гэдэг түлхүүр үгийг нь хасна. Ийм фунĸцыг arrow function гэдэг. Хэрвээ фунĸцын body нэгээс олон мөртэй байвал arrow function болгож болохгүйг анхаараарай 😉 .

Summary - Дүгнэлт

  • Фунĸц нь олон давтах асуудлын эсрэг шийдэл юм.
  • Фунĸц нь return type, function name, parameters, return type-с бүрддэг.
  • return type нь хамгийн эхэнд бичигддэг, фунĸцын output нь ямар төрөлтэй байхыг зааж өгдөг.
  • function name нь фунĸцын нэрийг хүссэнээрээ өгдөг.
  • parameters нь фунĸцын input болдог, бөгөөд хаалтан дотор бичдэг.
  • return value нь фунĸцын output учраас return type-тайгаа таарч байх ёстой.