Ruby - 自动完成算法的想法

时间:2016-07-01 11:20:19

标签: ruby algorithm

我目前正在研究街道名称的自动填充算法。 我得到一个文件作为参数,这是我的地址格式:

City,<SPACE>StreetNumber<SPACE>StreetName\n

城市和街道名称可以完整的单词填写(所以如果城市是“拉罗谢尔”,如果读“L”或“R”,算法应显示“La Rochelle”)

我当时从STDIN 1字符获得用户输入。

如果你不理解,这里有一些例子:

    /B-ADM-442> ./autoCompletion exampleDict 2>\dev\null < test2
    l
    i
    v
    2

    /B-ADM-442> ./autoCompletion exampleDict 2>\dev\null < test2
    {m} {l} {p} {s} {d}
    {Li} {Ly}
    {LILLE, d} {LILLE, v} {LILLE, g} {LILLE, h} {LILLE, p}
    {1 :  LILLE, 30 rue VICTOR danel} {2 :  LILLE, 120 boulevard VICTOR hugo}
    => Lille, 120 boulevard Victor Hugo

目前我只能完成城市所以“LILLE”,但现在我有点卡住了,我不知道怎么做其余的完整。

如果您有一些问题,请不要犹豫。

到目前为止,这是我的代码,它有点大,但我有点像红宝石菜鸟:)

    #!/usr/bin/env ruby
    #coding: utf-8

    require 'set'

    ##  ___________ _____ _____ 
    ## |_   _| ___ \_   _|  ___|
    ##    | | | |_/ / | | | |__  
    ##    | | |    /  | | |  __| 
    ##    | | | |\ \ _| |_| |___ 
    ##    \_/ \_| \_|\___/\____/ 
    ##

    class Trie

      attr_accessor :children, :value, :flag

      def initialize value=nil
        @children = {}
        @value = value
        @flag = false
      end

      def add char
        val = value ? value + char : char
        children[char] = Trie.new val
      end

      def insert word
        node = self
        word.each_char do |char|
          node.add char if not node.children.has_key? char
          node = node.children[char]
        end
        node.flag = true
      end

      def find word
        node = self
        word.each_char do |char|
          return nil if not node.children.has_key? char
          node = node.children[char]
        end
        return node.value
      end

      def all_prefixes
        results = Set.new
        results.add value if flag
        return results if children.empty?

        ap = children.values.collect {|node| node.all_prefixes}

        reduced = ap.reduce {|a,b| a.merge b}
        reduced or results
      end

      def autocomplete prefix
        node = self
        prefix.each_char do |char|
          return Set.new if not node.children.has_key? char
          node = node.children[char]
        end
        return node.all_prefixes
      end

    end

    ## ______ _____ _____ _____ _____ _____ _   _  _   _           ___          ________   __
    ## |  _  \_   _/  __ \_   _|_   _|  _  | \ | || \ | | / _ \ | ___ \         \ / /
    ## | | | | | | | /  \/ | |   | | | | | |  \| ||  \| |/ /_\ \| |_/ /\         V / 
    ## | | | | | | | |     | |   | | | | | | . ` || . ` ||  _  ||    /          \ /  
    ## | |/ / _| |_| \__/\ | |  _| |_\ \_/ / |\  || |\  || | | || |\ \          | |  
    ## |___/  \___/ \____/ \_/  \___/ \___/\_| \_/\_| \_/\_| |_/\_| \_|         \_/  
    ##

    class Dictionnary

      def initialize file, trieCity, trieStreet
        @validLine = /[a-zA-ZáàâäãåçéèêëíìîïñóòôöõúùûüýÿæœÁÀÂÄÃÅÇÉÈÊËÍÌÎÏÑÓÒÔÖÕÚÙÛÜÝŸÆŒ' ._-]+, \d+ [a-zA-Z0-9áàâäãåçéèêëíìîïñóòôöõúùûüýÿæœÁÀÂÄÃÅÇÉÈÊËÍÌÎÏÑÓÒÔÖÕÚÙÛÜÝŸÆŒ' ._-]+/
        @streetTypes = Array["allée", "avenue", "boulevard", "chemin", "impasse", "place", "quai", "rue", "square"]
        @regCity = /[a-zA-ZáàâäãåçéèêëíìîïñóòôöõúùûüýÿæœÁÀÂÄÃÅÇÉÈÊËÍÌÎÏÑÓÒÔÖÕÚÙÛÜÝŸÆŒ' ._-]+/
        @regStreetName = /[a-zA-ZáàâäãåçéèêëíìîïñóòôöõúùûüýÿæœÁÀÂÄÃÅÇÉÈÊËÍÌÎÏÑÓÒÔÖÕÚÙÛÜÝŸÆŒ' ._-]+, \d+ ([a-zA-Z0-9áàâäãåçéèêëíìîïñóòôöõúùûüýÿæœÁÀÂÄÃÅÇÉÈÊËÍÌÎÏÑÓÒÔÖÕÚÙÛÜÝŸÆŒ' ._-]+)/
        @regStreetNumber = /[a-zA-ZáàâäãåçéèêëíìîïñóòôöõúùûüýÿæœÁÀÂÄÃÅÇÉÈÊËÍÌÎÏÑÓÒÔÖÕÚÙÛÜÝŸÆŒ' ._-]+, (\d+) [a-zA-Z0-9áàâäãåçéèêëíìîïñóòôöõúùûüýÿæœÁÀÂÄÃÅÇÉÈÊËÍÌÎÏÑÓÒÔÖÕÚÙÛÜÝŸÆŒ' ._-]+/
        @trieCity = trieCity
        @trieStreet = trieStreet
        @trieLine = Trie.new
        @f_letter = Array.new
        @city = String.new
        @street = String.new
        @cityComplete = false
        begin
          @file = File.new(file.to_s, "r")
        rescue => err
          puts "Invalid argument"
          exit 84
        end
        counter = 0
        while line = @file.gets
          if @validLine.match(line)
            line[@regCity].split.each {|words|                         @f_letter.push(words.downcase)}
            line[@regCity].split.each {|words|         @trieCity.insert(words.downcase)}
            street = line.scan(@regStreetName).to_s.split[0].match(@regCity)
            @trieStreet.insert(line.scan(@regStreetName).last.first)
          end
          counter += 1
        end
        if counter == 0
          puts "Invalid argument"
          exit 84
        end
      end

      def prompt
        hash = @f_letter.each.each_with_object(Hash.new(0)) {|word, hsh|         hsh[word[0].downcase] += 1}
        hash = Hash[hash.sort_by {|k, v| [-v, k] }]
        counter = 1
        hash.each do |k, _|
          print "{#{k}}"
          if counter < 5 && counter < hash.size
            print " "
          else
            print "\n"
            break
          end
          counter += 1
        end
      end

      def handle_input char
        if @cityComplete == false
          self.complete_city(char)
        else
          self.complete_street(char)
        end
      end

      def complete_city char
        @city.insert(@city.size, char)
        array = @trieCity.autocomplete(@city)
        if array.size == 1
          @city = array.first.dup
          @cityComplete = true
          return
        end
        counter = 1
        array.each do |word|
          print "{#{word.chars.first((@city.size) + 1).join.capitalize}}"
          if counter < 5 && counter < array.size
            print " "
          else
            print "\n"
            break
          end
          counter += 1
        end
      end

      def complete_street char
        puts @city
        puts char
        @street.insert(@street.size, char)
        array = @trieStreet.autocomplete(@street)
        puts array.inspect
      end
    end

    ##  ___________ _____ _____ _____ _   _  _____ 
    ## |  _  | ___ \_   _|_   _|  _  | \ | |/  ___|
    ## | | | | |_/ / | |   | | | | | |  \| |\ `--. 
    ## | | | |  __/  | |   | | | | | | . ` | `--. \
    ## \ \_/ / |     | |  _| |_\ \_/ / |\  |/\__/ /
    ##  \___/\_|     \_/  \___/ \___/\_| \_/\____/

    if !ARGV[0]
      puts "Invalid argument"
      exit 84
    end

    if ARGV[0] == "-h"
      puts "USAGE"
      puts "\t./autocompletion dictionnary\n\n"
      puts "DESCRIPTION"
      puts "\tdictionnary\tfile, containing one address per line,         serving as knowledge base"
      exit 0
    end

    ## ___  ___  ___  _____ _   _ 
    ## |  \/  | / _ \|_   _| \ | |
    ## | .  . |/ /_\ \ | | |  \| |
    ## | |\/| ||  _  | | | | . ` |
    ## | |  | || | | |_| |_| |\  |
    ## \_|  |_/\_| |_/\___/\_| \_/


    trie1 = Trie.new
    trie2 = Trie.new
    dictionnary = Dictionnary.new(ARGV[0], trie1, trie2)
    dictionnary.prompt
    while user_input = STDIN.gets
      user_input ||= ''
      user_input.chomp!
      case user_input
      when "ABORT"
        exit 0
      else
        if user_input.size > 1
          exit 84
        end
        dictionnary.handle_input(user_input[0])
      end
    end

谢谢, 托马斯

1 个答案:

答案 0 :(得分:0)

为了帮助您入门,请查看readlines,并结合使用abbrev(均来自标准库)。

require "abbrev"
require "readline"  
arr = ["Marseille", "Marsan", "Martin"]

Readline.completion_proc = arr.abbrev.to_proc

while buffer = Readline.readline(">", true) # true means: keep history.
  exit if buffer.strip == "quit"
  p buffer
end
  • 按Tab键自动完成,所以&#34; Mart(tab)&#34;结果马丁
  • 可通过上下键访问历史记录
  • hash.to_proc相对较新,可能无法使用。