Бозиҳо созед ва илмҳои компютериро омӯзед.
Сохторҳои маълумот заминаи таҳияи самараноки нармафзорро ташкил медиҳанд, аммо усулҳои анъанавии омӯзиш аксар вақт онҳоро аз барномаҳои ҷаҳонии воқеӣ абстракт ва ҷудошуда ҳис мекунанд. Ин мақола равиши дигарро мегирад - омезиши назария бо татбиқи амалӣ дар Флуттер барои омӯзиши ҳам ҷолиб ва ҳам амалӣ .
Ин мутобиқшавӣ барои Flutter аз CS Applied Google бо Android илҳом гирифта, роҳи интерактивии фаҳмидани Arrays, HashSets ва HashMaps -ро фароҳам меорад. Ҳамагӣ дар тӯли 3-4 соат , шумо ҳангоми истифодаи онҳо дар контексти пурмазмун дар бораи ин сохторҳои асосии додаҳо фаҳмиши амиқ хоҳед гирифт.
Новобаста аз он ки шумо навгониҳо ҳастед, ки асосҳои CS-и худро мустаҳкам кунед ё таҳиягари ботаҷрибае, ки ҳадафи такмил додани малакаҳои шумост, ин дастур роҳи муассир ва ҷолиби азхудкунии сохторҳои муҳими додаҳоро пешниҳод мекунад. Биёед оғоз кунем.
Мо дар кори семинар чанд сохтори маълумотро истифода хоҳем кард, аз ин рӯ лутфан Lists , HashSets ва HashMaps -ро аз назар гузаронед. Шумо бояд боварӣ дошта бошед, ки бо истифода аз ин сохторҳои додаҳо дар Dart мавҷудияти унсурҳоро ворид кунед, нест кунед, дастрас кунед ва тафтиш кунед.
Ин як муқаддимаи хурд ба сохторҳои додаҳо, HashSets ва HashMap аст.
Ҳамчун намунаи фаъолият бо истифода аз HashMaps, барномае созед (на ҳатман барномаи Flutter — сатри фармон хуб аст), ки рамзи се ҳарфи кишварро мегирад (ниг. ISO-3166 ) ва номи пурраи кишваре, ки ба он тааллуқ дорад, баргардонад.
Барои намуна:
Input | Output ----- | ---------------------------------------------------- GBR | United Kingdom of Great Britain and Northern Ireland IDN | Indonesia IND | India
Ҳамчун васеъ, агар вуруд аз 3 ҳарф зиёд бошад, онро ҳамчун номи кишвар ҳисоб кунед ва рамзи се ҳарфро барои он баргардонед. Паёми хатогии муфидро нависед, агар вуруд на рамзи дуруст бошад ва на номи кишвар.
Биёед оғоз кунем.
Анаграмма калимаест, ки дар натиҷаи ҷойгиркунии ҳарфҳои калимаи дигар сохта шудааст. Масалан, кино як анаграммаи яхдон аст.
Бозӣ ба корбар калимаеро аз луғат медиҳад.
Истифодабаранда кӯшиш мекунад, ки ҳарчи бештар калимаҳое созад, ки дорои ҳамаи ҳарфҳои калимаи додашуда ва як ҳарфи иловагӣ бошанд. Дар хотир доред, ки илова кардани ҳарфи изофӣ дар аввал ё охири бе тартиби дигар ҳарфҳои дигар нодуруст аст. Масалан, агар бозӣ калимаи "маъдан" -ро ҳамчун оғозкунанда интихоб кунад, корбар метавонад "роз" ё "сифр" -ро тахмин кунад, аммо "дард" нест.
Истифодабаранда метавонад таслим шавад ва калимаҳоеро, ки онҳо тахмин накардаанд, бубинанд.
Мо ба шумо коди ибтидоиро пешниҳод кардем, ки дорои луғати 10,000-калима буда, қисмҳои UI-и ин бозӣро идора мекунад ва шумо барои навиштани AnagramBlocclass масъул хоҳед буд, ки ҳама коркарди калимаҳоро идора мекунад.
Рамзи ибтидоӣ аз се синфи асосии тир иборат аст:
anagrams_page.dart
Ин рамзи оддӣ аст, ки экранеро, ки мо дар боло мебинем, ифода мекунад. Мо блокро барои идоракунии давлатӣ барои экран истифода хоҳем кард. Мо бо насб кардани бозӣ тавассути оташ додани як ҳодиса ба блок ва муайян кардани он, ки экран ба ҳолати гуногуни бозӣ вокуниш нишон медиҳад, оғоз мекунем.
import 'package:anagrams/anagrams/bloc/anagram_bloc.dart'; import 'package:anagrams/anagrams/domain/word.dart'; import 'package:anagrams/l10n/l10n.dart'; import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class AnagramsPage extends StatelessWidget { const AnagramsPage({super.key}); @override Widget build(BuildContext context) { return BlocProvider( create: (_) => AnagramBloc() ..add( SetupAnagrams( DefaultAssetBundle.of(context), ), ), child: const AnagramsView(), ); } } class AnagramsView extends StatelessWidget { const AnagramsView({super.key}); @override Widget build(BuildContext context) { final l10n = context.l10n; return Scaffold( appBar: AppBar(title: Text(l10n.anagramAppBarTitle)), body: BlocBuilder<AnagramBloc, AnagramState>( builder: (context, state) { switch (state.status) { case AnagramGameStatus.gameError: return const Center( child: Text('An error occurred'), ); case AnagramGameStatus.loaded: return Padding( padding: const EdgeInsets.all(20), child: ListView( children: const [ _SelectedWord(), SizedBox(height: 20), _AnagramsTextField(), SizedBox(height: 10), _GuessListView(), ], ), ); case AnagramGameStatus.initial: return const Center( child: CircularProgressIndicator(), ); } }, ), floatingActionButton: const _NextWordButton(), ); } } class _SelectedWord extends StatelessWidget { const _SelectedWord(); @override Widget build(BuildContext context) { return BlocSelector<AnagramBloc, AnagramState, String>( selector: (state) => state.currentWord, builder: (context, currentWord) { return Text.rich( TextSpan( text: 'Find as many words as possible that can be ' 'formed by adding one letter to ', children: [ TextSpan( text: currentWord.toUpperCase(), style: const TextStyle(fontWeight: FontWeight.bold), ), TextSpan( text: ' (but that do not contain the substring' ' ${currentWord.toUpperCase()}).', ), ], ), ); }, ); } } class _AnagramsTextField extends StatelessWidget { const _AnagramsTextField(); @override Widget build(BuildContext context) { final controller = TextEditingController(); return TextField( controller: controller, decoration: const InputDecoration( hintText: 'Enter an anagram', border: OutlineInputBorder(), ), keyboardType: TextInputType.text, textInputAction: TextInputAction.done, onSubmitted: (value) { controller.clear(); context.read<AnagramBloc>().add(ProcessWord(value)); }, ); } } class _GuessListView extends StatelessWidget { const _GuessListView(); @override Widget build(BuildContext context) { return BlocSelector<AnagramBloc, AnagramState, List<Word>>( selector: (state) => state.guesses, builder: (context, guesses) { return Column( children: guesses.map((word) { return ListTile( minTileHeight: 0, contentPadding: EdgeInsets.zero, visualDensity: VisualDensity.compact, title: Text(word.value), leading: Icon( word.isAnagram ? Icons.check : Icons.close, color: word.isAnagram ? Colors.green : Colors.red, ), ); }).toList(), ); }, ); } } class _GameResult extends StatelessWidget { const _GameResult(this.currentWord, this.result); final List<Word> result; final String currentWord; @override Widget build(BuildContext context) { return ListView( shrinkWrap: true, children: [ const SizedBox(height: 20), Padding( padding: const EdgeInsets.symmetric( horizontal: 20, ), child: Text( 'Game Result for $currentWord', style: const TextStyle(fontSize: 20), ), ), Padding( padding: const EdgeInsets.all(20), child: SizedBox( width: double.infinity, child: DataTable( decoration: BoxDecoration( border: Border.all( color: Colors.grey.shade400, ), borderRadius: BorderRadius.circular(10), ), columns: const [ DataColumn(label: Text('Possible Anagrams')), DataColumn(label: Text('Your Guesses')), ], rows: result.map((word) { return DataRow( cells: [ DataCell(Text(word.value)), DataCell( Center( child: Icon( word.isAnagram ? Icons.check : Icons.close, color: word.isAnagram ? Colors.green : Colors.red, ), ), ), ], ); }).toList(), ), ), ), ], ); } } class _NextWordButton extends StatelessWidget { const _NextWordButton(); @override Widget build(BuildContext context) { final l10n = context.l10n; return BlocPresentationListener<AnagramBloc, AnagramPresenterEvent>( listener: (context, event) { if (event is FinishGuess) { // show a bottom sheet with the anagrams that were not guessed showModalBottomSheet<void>( context: context, useSafeArea: true, builder: (context) { return _GameResult(event.currentWord, event.result); }, ); } }, child: FloatingActionButton.extended( onPressed: () async { context.read<AnagramBloc>().add(ResetGame()); }, label: Text(l10n.nextWordButton), ), ); } }
_SelectWord
: калимаи интихобшударо барои бозӣ нишон медиҳад, то аз он анаграммаҳо ташкил кунад.
_AnagramsTextField
: калимаро қабул мекунад ва ҳодисаро барои коркарди калимаи чопкардаи корбар оғоз мекунад.
_GuessListView
: тахминҳои воридкардаи корбарро нишон медиҳад ва оё онҳо дурустанд ё не.
_NextWordButton
: бозиро аз нав танзим мекунад ва ба корбар тамоми анаграммаҳои калимаи ҷорӣ ва кадомеро, ки корбар тахмин кардааст, пешкаш мекунад.
anagram_states.dart
enum AnagramGameStatus { initial, loaded, gameError } const minNumAnagrams = 5; const defaultWordLength = 3; const maxDefaultWordLength = 7; @immutable final class AnagramState extends Equatable { factory AnagramState({ AnagramGameStatus status = AnagramGameStatus.initial, List<String> words = const [], String currentWord = '', List<String> anagrams = const [], List<Word> guesses = const [], HashSet<String>? wordSet, HashMap<String, List<String>>? anagramMap, HashMap<int, List<String>>? sizeToWords, int wordLength = defaultWordLength, }) { return AnagramState._( status: status, words: words, currentWord: currentWord, anagrams: anagrams, guesses: guesses, wordSet: wordSet ?? HashSet<String>(), anagramMap: anagramMap ?? HashMap<String, List<String>>(), sizeToWords: sizeToWords ?? HashMap<int, List<String>>(), wordLength: wordLength, ); } const AnagramState._({ required this.status, required this.words, required this.currentWord, required this.anagrams, required this.guesses, required this.wordSet, required this.anagramMap, required this.sizeToWords, this.wordLength = defaultWordLength, }); // The current status of the game final AnagramGameStatus status; // All the words in the game final List<String> words; // Currently chosen word of the game to form anagrams final String currentWord; // All the anagrams for the current word final List<String> anagrams; // All the guesses user has made final List<Word> guesses; // A set of all the words in the game final HashSet<String> wordSet; // A map of anagrams for each word final HashMap<String, List<String>> anagramMap; // Stores the words in increasing order of their length final HashMap<int, List<String>> sizeToWords; final int wordLength; AnagramState copyWith({ AnagramGameStatus? status, List<String>? words, String? currentWord, List<String>? anagrams, List<Word>? guesses, HashSet<String>? wordSet, HashMap<String, List<String>>? anagramMap, HashMap<int, List<String>>? sizeToWords, int? wordLength, }) { return AnagramState( status: status ?? this.status, words: words ?? this.words, currentWord: currentWord ?? this.currentWord, anagrams: anagrams ?? this.anagrams, guesses: guesses ?? this.guesses, wordSet: wordSet ?? this.wordSet, anagramMap: anagramMap ?? this.anagramMap, sizeToWords: sizeToWords ?? this.sizeToWords, wordLength: wordLength ?? this.wordLength, ); } @override List<Object?> get props => [ status, words, currentWord, anagrams, guesses, wordSet, anagramMap, sizeToWords, wordLength, ]; }
AnagramState ҳамаи тағирёбандаҳои ҳолати заруриро барои иҷро кардани бозӣ нигоҳ медорад.
status
: нигоҳ медорад, ки оё бозӣ боркунӣ (аввалӣ), бор карда шудааст ё хатогие партофта шудааст.words
: Ҳама калимаҳоро аз файли word.txt, ки аз файл бор карда шудаанд, номбар мекунад.anagrams
: ҳамаи анаграммаҳоро барои калимаи интихобшуда нигоҳ медорад.currentword
: калимаи интихобшуда аз рӯйхати калимаҳо ва калима барои сохтани анаграммаҳо аз.guesses
: Ҳама интихобҳое, ки корбар ворид мекунад ва дуруст ё нодуруст будани онҳо.
Мо ба тафсилоти боқимонда, баъдтар дар мақола хоҳем расид.
anagram_bloc.dart
import 'dart:async'; import 'dart:collection'; import 'dart:convert'; import 'package:anagrams/anagrams/domain/word.dart'; import 'package:bloc_presentation/bloc_presentation.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; part 'anagram_events.dart'; part 'anagram_states.dart'; class AnagramBloc extends Bloc<AnagramEvent, AnagramState> with BlocPresentationMixin<AnagramState, AnagramPresenterEvent> { AnagramBloc() : super(AnagramState()) { on<SetupAnagrams>(_onSetupAnagrams); on<ProcessWord>(_onProcessWord); on<ResetGame>(_onResetGame); } Future<void> _onSetupAnagrams( SetupAnagrams event, Emitter<AnagramState> emit, ) async { try { // this should not be done here, // but for the sake of simplicity, we will do it here final wordsFile = await event.defaultAssetBundle.loadString('assets/words.txt'); // read each line in the file final words = const LineSplitter().convert(wordsFile); // change the state of the game emit( state.copyWith( status: AnagramGameStatus.loaded, words: words, ), ); // reset the game _onRestartGame(emit); } catch (e) { emit( state.copyWith( status: AnagramGameStatus.gameError, ), ); } } Future<void> _onProcessWord( ProcessWord event, Emitter<AnagramState> emit, ) async { try { final word = event.word.trim().toLowerCase(); if (word.isEmpty) { return; } if (_isGoodWord(word) && state.anagrams.contains(word)) { // remove the word from the list of anagrams // add the word to the list of guesses emit( state.copyWith( anagrams: state.anagrams..remove(word), guesses: [...state.guesses, Word(word, isAnagram: true)], ), ); // if there are no more anagrams, the game is over // call _onResetGame to reset the game if (state.anagrams.isEmpty) { add(ResetGame()); } } else { emit( state.copyWith( guesses: [...state.guesses, Word(word)], ), ); } } catch (e) { // show an error message } } FutureOr<void> _onResetGame(ResetGame event, Emitter<AnagramState> emit) { _onGameFinished(); _onRestartGame(emit); } void _onRestartGame(Emitter<AnagramState> emit) { final starterWord = _pickGoodStarterWord(emit); emit( state.copyWith( status: AnagramGameStatus.loaded, currentWord: starterWord, anagrams: _getAnagrams(starterWord), guesses: [], ), ); } void _onGameFinished() { emitPresentation(FinishGuess(_result, state.currentWord)); } List<Word> get _result { // All the anagrams that were not guessed final notGuessedAnagrams = state.anagrams.map(Word.new).toList(); // All the guesses that were made final guesses = state.guesses.where((word) => word.isAnagram).toList(); // return the list of anagrams that were not guessed return [...guesses, ...notGuessedAnagrams]; } /// create a function to find all the anagrams of the target word List<String> _getAnagrams(String targetWord) { // find all the anagrams of the target word final anagrams = <String>[]; // return the list of anagrams return anagrams; } // ignore: unused_element List<String> _getAnagramsWithOneMoreLetter(String targetWord) { final anagrams = HashSet<String>(); // return the list of anagrams return anagrams.toList(); } /// Picks a good starter word for the game. String _pickGoodStarterWord(Emitter<AnagramState> emit) { const word = 'skate'; return word; } /// Checks if the word is a good word. bool _isGoodWord(String word) { return true; } }
onSetupAnagrams
: Файлро мехонад, калимаҳоро тақсим мекунад ва онҳоро ба рӯйхат илова мекунад. Он инчунин калимаи ҷориро пайдо мекунад, анаграммаҳоро барои ин калимаи интихобшуда пайдо мекунад ва ҳолатро нав мекунад.
onProcessWord
: Ин коркардкунандаест, ки вақте корбар тахмин ворид мекунад ва ҳолатро нав мекунад.onReset
: Ҳангоми пахш кардани тугмаи калимаи навбатӣ занг зада, бозиро аз нав барқарор мекунад.isGoodWord
: Тасдиқ мекунад, ки калимаи додашуда дар луғат мавҷуд аст ва бо илова кардани ҳарф ба аввал ё охири калимаи асосӣ сохта нашудааст.getAnagrams
: Рӯйхати ҳамаи анаграммаҳои имконпазири калимаи додашударо эҷод мекунад.getAnagramsWithOneMoreLetter
: Рӯйхати ҳамаи калимаҳои имконпазирро эҷод мекунад, ки онҳоро бо илова кардани як ҳарф ба калимаи додашуда сохтан мумкин аст.pickGoodStarterWord
: Тасодуфан калимаеро бо ҳадди ақалл шумораи дилхоҳи анаграммаҳо интихоб кунед.Марҳилаи аввал ба эҷоди як барномаи хеле соддаи корӣ нигаронида шудааст. Шумо асосҳоеро амалӣ хоҳед кард, ки дар навбати худ дар марҳилаҳои 2 ва 3 сохта мешаванд.
Мо дар anagram_bloc.dart
кор хоҳем кард.
Татбиқи getAnagrams
, ки сатр мегирад ва ҳамаи анаграммаҳои ин сатрро дар вуруди мо пайдо мекунад. Стратегияи мо дар айни замон содда хоҳад буд: танҳо ҳар як сатрро дар words
Рӯйхат бо калимаи воридшуда муқоиса кунед, то муайян кунед, ки оё онҳо анаграмма мебошанд. Аммо мо инро чӣ тавр кунем?
Стратегияҳои гуногун мавҷуданд, ки шумо метавонед барои муайян кардани он, ки ду сатр анаграммаи якдигаранд (ба монанди ҳисоб кардани шумораи пайдоиши ҳар як ҳарф), аммо барои ҳадафи мо шумо функсияи ёрирасон эҷод мекунед (онро sortLetters
меномед), ки String
мегирад ва String
дигарро бо ҳарфҳои якхела бо тартиби алифбо бармегардонад (масалан, "пост" -> "пост").
Муайян кардани он, ки оё ду сатр анаграмма мебошанд, масъалаи оддии тафтиши дарозии якхела будани онҳо (ба хотири суръат) ва санҷидани он, ки версияҳои мураттабшудаи ҳарфҳои онҳо баробаранд.
Мутаассифона, стратегияи мустақим барои мо барои амалӣ кардани қисми боқимондаи ин бозӣ хеле суст хоҳад буд. Ҳамин тавр, ба мо лозим меояд, ки onSetupAnagrams
худро аз нав дида бароем ва баъзе сохторҳои додаҳоро пайдо кунем, ки калимаҳоро ба тарзе, ки барои мақсадҳои мо мувофиқанд, нигоҳ доранд. Мо ду сохтори нави маълумотро эҷод хоҳем кард (ба ғайр аз words
):
HashSet
( wordSet
номида мешавад), ки ба мо имкон медиҳад, ки зуд (дар O(1)) дуруст будани калимаро тафтиш кунем.
HashMap
(бо номи anagramMap
), ки ба мо имкон медиҳад, ки анаграммаҳоро гурӯҳбандӣ кунем. Мо ин корро бо истифода аз версияи sortLetters
сатр ҳамчун калид ва нигоҳ доштани Рӯйхати калимаҳое, ки ба он калид мувофиқанд, ҳамчун арзиши мо анҷом медиҳем. Масалан, мо метавонем як вуруди шакл дошта бошем: калид: арзиши "opst": ["пост", "нуқта", "дегҳо", "боло", ...].
Ҳангоми коркарди калимаҳои воридотӣ, ба ҳар яки онҳо sortLetters
занг занед ва санҷед, ки оё anagramMap
аллакай сабти ин калидро дар бар мегирад. Агар ин тавр бошад, калимаи ҷорӣро дар он калид ба List
илова кунед. Дар акси ҳол, калимаи нав эҷод кунед, калимаро ба он илова кунед ва онро дар HashMap
бо калиди мувофиқ нигоҳ доред.
Вақте ки шумо ин корро анҷом додед, шумо ба охири марҳилаи 1 расидед! Шумо ҳоло омодаед, ки ба марҳилаи дуюм гузаред, ки дар он шумо ба барномаи худ мураккабии бештар илова мекунед.
Milestone 2 аз он иборат аст, ки калимаҳои интихобшуда барои бозии анаграмма мувофиқанд. Баръакси. Марҳилаи қаблӣ, ин ба се бахш тақсим шудааст.
isGoodWord
Вазифаи навбатии шумо татбиқи isGoodWord
аст, ки тафтиш мекунад:
wordSet
) ва
Санҷиши он, ки оё калимаи луғат дуруст аст ё wordSet
. Санҷиши он, ки калима калимаи асосиро ҳамчун зерсатр дар бар намегирад, ҳамчун мушкилот боқӣ мемонад!
getAnagramsWithOneMoreLetter
Дар ниҳоят, getAnagramsWithOneMoreLetter
ро татбиқ кунед, ки сатрро мегирад ва ҳама анаграммаҳоеро, ки бо илова кардани як ҳарф ба ин калима сохта мешаванд, пайдо мекунад.
Боварӣ ҳосил кунед, ки List
навро ҳамчун арзиши бозгашти худ эҷод кунед ва пас калимаи додашуда + ҳар як ҳарфи алифборо як ба як бо воридот дар anagramMap
тафтиш кунед.
Инчунин, onRestartGame
навсозӣ кунед, то ба ҷои getAnagrams
getAnagramsWithOneMoreLetter
даъват кунед.
pickGoodStarterWord
Агар бозии шумо кор кунад, ба татбиқи pickGoodStarterWord
идома диҳед, то бозӣ ҷолибтар шавад. Дар калимаи Рӯйхат нуқтаи ибтидоии тасодуфиро интихоб кунед ва ҳар як калимаро дар массив тафтиш кунед, то он даме, ки ҳадди аққал minNumAnagrams
дошта бошед. Боварӣ ҳосил кунед, ки агар лозим бошад, печонидани атрофи онро то оғози массив анҷом диҳед.
Аз се ду хиссаи рох! Танҳо як марҳила ва тамдиди пеш аз анҷоми шумо.
Дар ин лаҳза, бозӣ функсионалӣ аст, аммо бозӣ метавонад хеле душвор бошад, агар шумо бо калимаи дарози асосӣ оғоз кунед. Барои роҳ надодан ба ин, биёед onSetupGame
рефактор кунем, то калимаҳои дарозиашон афзоиш ёбад.
Ин рефактор дар onSetupGame
оғоз мешавад, ки дар он ба ғайр аз пур кардани Рӯйхати word
, шумо инчунин бояд ҳар як калимаро дар HashMap
нигоҳ доред (бигзор онро sizeToWords
гӯем), ки дарозии калимаро ба List
ҳамаи калимаҳои дарозиашон харита мекунад. Ин маънои онро дорад, ки масалан, шумо бояд ҳамаи калимаҳои чаҳорҳарфаро дар луғат тавассути занг задан ба sizeToWords.get(4)
дастрас кунед.
Дар pickGoodStarterWord
, ҷустуҷӯи худро бо калимаҳои дарозии wordLength
маҳдуд кунед ва пас аз анҷоми кор, wordLength
зиёд кунед (агар он аллакай дар axWordLength
набошад), то даъвати навбатӣ калимаи калонтареро баргардонад.
Ин фаъолият (мисли ҳама фаъолиятҳои оянда) дорои баъзе васеъшавии ихтиёрӣ мебошад. Агар вақт иҷозат диҳад, ҳадди аққал як васеъкуниро аз рӯйхати дар поён овардашуда ё худатон ихтироъ кардаед.
wordSet
боқӣ монанд, зеро онҳо метавонанд ҳамчун анаграмма ба ибораи дигар истифода шаванд.
Табрикот, ки то ба охир расидед! Шумо омӯхтаед, ки чӣ гуна Lists, HashSets ва HashMaps коркарди пурсамари додаҳо дар Flutter-ро ба мисли онҳо дар ҳама гуна замимаи хуб оптимизатсияшуда иҷро мекунанд. Фаҳмидани ин сохторҳо ба шумо дар навиштани рамзи миқёспазир ва иҷрошаванда бартарӣ медиҳад. Пас, пеш равед ва ба пушти худ як зарбаи сазовор диҳед! Акнун, ин донишро дар амал татбиқ кунед ва чизҳои аҷибро эҷод кунед!