javascript

Алгоритм для запоминания иностранных слов

  • четверг, 10 августа 2017 г. в 03:11:55
https://habrahabr.ru/post/335212/
  • Яндекс API
  • Разработка под Android
  • Разработка мобильных приложений
  • Алгоритмы
  • JavaScript


На данный момент создано множество приложений для запоминания слов. Из тех что мне запомнились могу выделить такие Android приложения как Lingualeo, Английские слова, СловоУч.

Главным недостатком этих приложений для меня был платный аккаунт для добавления своей базы слов. Поэтому встал вопрос о написании своего приложения для запоминания слов. Главной идеей было подключения внешнего API словаря и переводчика для переводов слов на родной язык. В качестве такого API было выбрано Yandex API (API Переводчика и API Словаря).

Первым делом необходимо было получить ключи разработчика. Для переводчика и для словаря.

В качестве языка и платформы разработки был выбран JavaScript и библиотека JQuery.

Для получения перевода слова на нужный язык я использовал следующий код:

            var oneWord = function() {
                $.post("https://dictionary.yandex.net/api/v1/dicservice.json/lookup",
                    {
                        key: apiKey, 
                        lang: lang, 
                        text: words[index].text
                    }, function(data) 
                    {
                        words[index].tr = "";
                        words[index].ts = "";
                        for (var j = 0; j < data.def.length; j++) {
                            var def = data.def[j];
                            for (var k = 0; k < def.tr.length; k++) {
                                var tr = def.tr[k];
                                words[index].tr += tr.text + "; ";
                            }					
                            if (def.ts)
                                words[index].ts = def.ts;
                        }
                        if (words[index].tr == "") {
                            translateWords();
                            tsWords();
                            return;
                        } else {
                            var str = words[index].tr;
                            words[index].tr = str.substring(0, str.length - 2);
                        }
                        complete();
                    }, 
                "json");
            };
            var tsWords = function() {
                var text = words[index].text;
                var tsText = "";
                var tsWords = text.match(/\S+/gi);
                var tsIndex = 0;
                var tsPost = function() {
                    $.post("https://dictionary.yandex.net/api/v1/dicservice.json/lookup",
                        {
                            key: apiKey, 
                            lang: lang, 
                            text: tsWords[tsIndex]
                        }, function(data) 
                        {
                            var ts = "";
                            for (var j = 0; j < data.def.length; j++) {
                                    var def = data.def[j];
                                    if (def.ts)
                                            ts = def.ts;
                            }
                            tsText += ts + " ";
                            if ((tsIndex < (tsWords.length - 1)) && (tsIndex < 5)) {
                                tsIndex++;
                                tsPost();
                            } else {
                                words[index].ts = tsText.trim();
                                complete(false, true);
                            }
                        },
                    "json");
                };
                tsPost();
            };
            var translateWords = function() {
                $.post("https://translate.yandex.net/api/v1.5/tr.json/translate",
                    {
                        key: apiKeyTranslate, 
                        lang: slang, 
                        text: words[index].text
                    }, function(data) 
                    {
                        words[index].tr = "";
                        for (var j = 0; j < data.text.length; j++) {
                            var text = data.text[j];
                            words[index].tr += text + "; ";
                        }
                        var str = words[index].tr;
                        words[index].tr = str.substring(0, str.length - 2);
                        complete(true, false);
                    },
                "json");
            };
            var qu = function() {
                if (!words[index].tr) {
                    oneWord();
                } else {
                    complete();
                } 
            };
            qu();

Тут функция oneWord переводит одно слово, tsWords находит транскрипции первых пяти слов в выражении (если дано не слово, а предложение), translateWords переводит предложение.

Результирующая функция complete вызывается для заполнения формы слова с транскрипцией и переводом:

        var complete = function(tr, ts) {
            if (ts == undefined) ts = true;
            if (tr == undefined) tr = true;
            var word = words[index];
            if (tr) $("#text").html(word.text);
            if (ts) $("#ts").html("[" + word.ts + "]");
            $("#tr").hide();
            $("#attempt").hide();
            $("#show").show();
            $("#tr").html(word.tr);
            $("#tts").show();
        };

В массиве слов words index отражает текущее слова для запоминания. Следующее слово выбирается по следующему алгоритму:

        var words = [],
            patternCount = 5,
            indexMemory = {},
            indexMemoryCount = 0,
            patternIndex = [],
            lastIndex = -1,
            lastIndexs = [],
            lastIndexsCount =  2,
            wasAttempt = false,
            wasMemory = false,
            deep = 0,
            deepMax = 100; 

        var index = nextIndex();

        var nextIndex = function() {
            deep++;
            if (lastIndexsCount - words.length >= 0) {
                lastIndexsCount = 0;
            } 
            if ((patternIndex.length < patternCount) && (indexMemoryCount < words.length)) {
                if (deep > deepMax) {
                    
                    var index = maxAttemptsIndex(true);
                    return index;
                }
                var index = Math.floor(Math.random() * words.length);
                if (indexMemory[index]) {
                    return nextIndex();
                }
                indexMemory[index] = "do";
                indexMemoryCount++;
                patternIndex.push(index);
                lastIndex = index;
                pushIndex(lastIndex);
                return index;
            } else {
                var index = Math.floor(Math.random() * (patternIndex.length + 1));
                if (index == patternIndex.length || (patternIndex.length == 0)) {
                    wasMemory = true;
                    var ind = maxAttemptsIndex();
                    if (inArray(lastIndexs, ind)) 
                    {
                        if (deep > deepMax) {
                            ind = Math.floor(Math.random() * words.length);
                            lastIndex = ind;
                            pushIndex(lastIndex);
                            return ind;
                        }
                        return nextIndex();
                    }
                    lastIndex = ind;
                    pushIndex(lastIndex);
                    return ind;
                }
                if (inArray(lastIndexs, patternIndex[index])) return nextIndex();
                lastIndex = patternIndex[index];
                pushIndex(lastIndex);
                return patternIndex[index];
            }
        };

        var maxAttemptsIndex = function(notAttempts) {
            var arr = sortMemoryIndexes(indexMemory);
            var index = getRandomFishIndex(arr, notAttempts);
            return index;
        };

       var pushIndex = function(index) {
            if (lastIndexsCount == 0) return;
            if (lastIndexs.length < lastIndexsCount) {
                lastIndexs.push(index);
            } else {
                lastIndexs[0] = lastIndexs[1];
                lastIndexs[1] = index;
            }
        };

        var inArray = function(arr, elem) {
            for (var i = 0; i < arr.length; i++) {
                if (arr[i] == elem) 
                    return true;
            }
            return false;
        };

        function getRandomFishIndex(arr, notAttempts) {
            var fishForLevel = arr;
            var fishTotalWeight = 0, fishCumWeight = 0, i;
            // sum up the weights
            for (i = 0; i < fishForLevel.length; i++) {
                fishTotalWeight += fishForLevel[i].attempts;
            }
            if (notAttempts) {
                fishTotalWeight = 0;
            }
            var random = Math.floor(Math.random() * fishTotalWeight);
            // now find which bucket out random value is in
            if (fishTotalWeight == 0) return fishForLevel[Math.floor(Math.random() * fishForLevel.length)].index;
            for (i = 0; i < fishForLevel.length; i++) {
                fishCumWeight += fishForLevel[i].attempts;
                if (random <= fishCumWeight) {
                    return fishForLevel[i].index;
                }
            }
        }

        function sortMemoryIndexes(indexMemory) {
            var arr = [];
            for (var key in indexMemory) {
                if (indexMemory[key] == "do") {
                    var word = jQuery.extend(true, {}, words[key]);
                    word.index = key;
                    arr.push(word);
                }
            }
            var sAttempt = function(first, second) {
                if (first.attempts < second.attempts)
                    return 1;
                else if (first.attempts > second.attempts)
                    return -1;
                else
                    return 0;
            };
            return arr.sort(sAttempt);
        }

Суть в том, что требуется выбрать следующее слово из набора ранее не изученных, а также предыдущих изученных слов. При этом вероятность показа последних должна быть больше, если слово плохо запоминается.

Именно кнопка «Неправильно» реализует перестановку вероятностей показа слов.

        $("#attempt").click(function()
        { 
            words[index].attempts++;
            wasAttempt = true;
            $("#attempt").hide();
        });

Данный метод запоминания слов показался мне наиболее эффективным. В остальном программный код приложения реализует события и действия элементов интерфейса. HTML и сопутствующий JavaScript код был обернут в Cordova для платформы Android.

Приложение «EnglishWords» позволяет учить английские слова и слова многих других языков. В программе имеется базовый набор слов для изучения. Главной особенностью программы является возможность создавать свои наборы слов для изучения. ИНФОРМАЦИЯ НА ЭКРАНЕ * Процент. Означает процент изученных слов в словаре. КАК ЭТО РАБОТАЕТ. Процесс изучения слов начинается с того, что программа набирает из выбранных словарей 5 случайных слов и начинает их показывать в случайном порядке. После того, как слова выучены из словаря извлекаются следующие 5 случайных слов. Если вы отвечаете не правильно на слово, то слово будет показываться чаще. Когда все слова выучены показываются только те слова на которые чаще всего давался не правильный ответ. СЛОВАРЬ Базовый набор слов содержит около 1000 наиболее употребляемых английских слов.

Приложение использует yandex и google api для получения перевода, транскрипции и звукового воспроизведения. Для работы приложения необходим доступ в интернет.

Приведу скриншоты приложения: