Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Saving People from Typos

Saving People from Typos

Yuki Nishijima

June 05, 2015
Tweet

More Decks by Yuki Nishijima

Other Decks in Programming

Transcript

  1. 4 B W J O H  1F P Q

    M F  G S P N  5Z Q P T
  2. !ZVLJ : 6 , *  / * 4 )

    * + * . "  ੢ ౢ  ༔ و
  3. !ZVLJ 4PGUXBSF&OHJOFFSBU : 6 , *  / * 4

    ) * + * . "  ੢ ౢ  ༔ و
  4. !ZVLJ .BJOUBJOFSPGkaminari : 6 , *  / * 4

    ) * + * . "  ੢ ౢ  ༔ و
  5. 4 B W J O H  1F P Q

    M F  G S P N  5Z Q P T
  6. 8 ) :  $ " / ` 5 

    3 6 # :  % 0  5 ) &  4 " . &
  7. 3 6 # :  * 4  % &

    4 * ( / & %  5 0  . " , &  1 3 0 ( 3 " . . & 3 4  ) " 1 1 :
  8. 8 ) :  $ " / ` 5 

    3 6 # :  % 0  5 ) &  4 " . &
  9. 5 I F  d i d _ y o

    u _ m e a n  H F N
  10. require "did_you_mean" "Yuki".starts_with?("Y") # => NoMethodError: undefined method # `starts_with?’

    for “Yuki":String # # Did you mean? start_with? # "Yuki".start_with?("Y") # => true
  11. ) 0 8  % 0 & 4  *

    5  8 0 3 ,
  12. w "  T Q F M M  D

    I F D L F S  w . P O L F Z  Q B U D I F T
  13. w "  T Q F M M  D

    I F D L F S  w . P O L F Z  Q B U D I F T
  14. * / 4 * % &  5 ) &

     4 1 & - -  $ ) & $ , & 3
  15. w %JDUJPOBSZ * / 4 * % &  5

    ) &  4 1 & - -  $ ) & $ , & 3
  16. w %JDUJPOBSZ w $POUSPMNFDIBOJTN * / 4 * % &

     5 ) &  4 1 & - -  $ ) & $ , & 3
  17. w %JDUJPOBSZ w $POUSPMNFDIBOJTN w 0QUJNJ[BUJPO * / 4 *

    % &  5 ) &  4 1 & - -  $ ) & $ , & 3
  18. % * $ 5 * 0 / " 3: w

    "TFUPGXPSET w "TQFMMDIFDLFSNBZIBWFTFWFSBMEJDUJPOBSJFT w 5IFDPOUFOUPGBEJDUJPOBSZNBZEJGGFSEFQFOEJOHPO UIFUZQFPGUIFTQFMMDIFDLFS
  19. $ 0 / 5 3 0 -  . &

    $ ) " / * 4 . w 1JDLTVQUIFNPTUMJLFMZTQFMMJOHDPSSFDUJPO T GPSUIFJOQVU w "TQFMMDIFDLFSNBZIBWFTFWFSBMDPOUSPMNFDIBOJTNT w 6TFTPOFPSNPSFNFUSJDT w TJNJMBSJUZCFUXFFOTUSJOHT FH-FWFOTIUFJO +BSP8JOLMFS  w OHSBN w /BJWFMZTDBOOJOHBMMUIFXPSETJOUIFEJDUJPOBSZXPVMECF QBJOGVMMZTMPX
  20. 0 1 5 * . * ; "5 * 0

    / w *NQSPWFTQFSGPSNBODFBOEPSBDDVSBDZ w 0QUJNJ[BUJPOUFDIOJRVFTNBZCFDPOUFYUTQFDJpD w (SBNNBSBOBMZTJT w 4UBUJTUJDBMNPEFM w 1SPOVODJBUJPOCBTFEDPSSFDUJPO
  21. l 5 I F Z  X F F S

    F  U S B W F M J O H  U I S P V H I  U I F  O J H I U  z
  22. XFSF XIFSF l 5 I F Z  X F

    F S F  U S B W F M J O H  U I S P V H I  U I F  O J H I U  z
  23. XFSF XIFSF ✅HSBNNBUJDBMMZDPSSFDU "JODPSSFDU l 5 I F Z 

    X F F S F  U S B W F M J O H  U I S P V H I  U I F  O J H I U  z
  24. l 5 I F Z  O P  I

    P X  U P  E P  J U  z
  25. LOPX ✅TBNFQSPOVODJBUJPO l 5 I F Z  O P

     I P X  U P  E P  J U  z
  26. 5 I F  d i d _ y o

    u _ m e a n  H F N w %JDUJPOBSZ w "TFUPGTZNCPMT w $POUSPMNFDIBOJTN w 6TFT-FWFOTIUFJOEJTUBODFUPDPSSFDUNJTUZQFEXPSET w 6TFT+BSP8JOLMFSEJTUBODFUPDPSSFDUNJTTQFMUXPSET w 0QUJNJ[BUJPO w $POUFYUCBTFEEJDUJPOBSZ
  27. . J T U Z Q F  $ P

    S S F D U J P O  . J T T Q F M M  $ P S S F D U J P O C O N T R O L M E C H A N I S M
  28. 8IZ/FFET5ZQFTPG$POUSPM.FDIBOJTN .JTUZQFEXPSET w 5IFDPSSFDUTQFMMJOHJTDPSSFDUMZSFNFNCFSFE CVUPOFPSNPSF JODPSSFDUMFUUFSTBSFUZQFECZNJTUBLF w DPSSFDUFECZUIFdid_you_meanHFNVTJOH-FWFOTIUFJOEJTUBODF .JTTQFMUXPSET w

    5IFDPSSFDUTQFMMJOHJTNJTSFNFNCFSFEPSOPUSFNFNCFSFEBUBMM w 5IFpSTUDIBSBDUFSJTBMXBZTDPSSFDU • Yannakoudakis, E.J. and Fawthrop, D., "The rules of spelling errors," Information Processing and Management, vol. 19, no. 2, pp. 87-99, 1983. w DPSSFDUFECZUIFdid_you_mean HFNVTJOH+BSP8JOLMFSEJTUBODF
  29. . J T U Z Q F  $ P

    S S F D U J P O  . J T T Q F M M  $ P S S F D U J P O
  30. - F W F O T I U F J

    O  % J T U B O D F
  31. start_with starts_with - F W F O T I U

    F J O  % J T U B O D F
  32. start_with starts_with - F W F O T I U

    F J O  % J T U B O D F 5IFEJTUBODFJT ✅JOTFSUJPO
  33. first_name full_name - F W F O T I U

    F J O  % J T U B O D F
  34. first_name full_name - F W F O T I U

    F J O  % J T U B O D F ✅ ✅ ✅ TVCTUJUVUJPO
  35. first_name full_name - F W F O T I U

    F J O  % J T U B O D F ✅ ✅ ✅ ✅ 5IFEJTUBODFJT EFMFUJPO TVCTUJUVUJPO
  36. . J T U Z Q F  $ P

    S S F D U J P O  . J T T Q F M M  $ P S S F D U J P O
  37. + B S P  8 J O L M

    F S  % J T U B O D F
  38. + B S P  % J T U B

    O D F   1 S F G J Y  # P O V T
  39. +BSP%JTUBODF m : 10 (the number of matching letters) t

    : 1 (half the number of transpositions)
  40. +BSP%JTUBODF m : 10 (the number of matching letters) t

    : 1 (half the number of transpositions)
  41. m : 10 (the number of matching letters) t :

    1 (half the number of transpositions) 5IFEJTUBODFJT0.9666… +BSP%JTUBODF
  42. I U U Q    H J U

     J P  W 3 E : 8 5 I F  E J E @ Z P V @ N F B O ` T  4 Q F M M  $ I F D L F S
  43. "  T Q F M M  D I

    F D L F S  . P O L F Z  Q B U D I F T ✅
  44. 07 & 3 3 * % * / ( 

    5 ) &  & 3 3 0 3  . & 4 4 "( & module DidYouMean module NameErrorExtension prepend_features NameError def to_s super + Formatter.new(corrections).to_s rescue super end def corrections SPELL_CHECKERS[self.class.to_s].new(self).corrections end end end
  45. module DidYouMean module NameErrorExtension prepend_features NameError def to_s super +

    Formatter.new(corrections).to_s rescue super end def corrections SPELL_CHECKERS[self.class.to_s].new(self).corrections end end end 07 & 3 3 * % * / (  5 ) &  & 3 3 0 3  . & 4 4 "( &
  46. module DidYouMean module NameErrorExtension prepend_features NameError def to_s super +

    Formatter.new(corrections).to_s rescue super end def corrections SPELL_CHECKERS[self.class.to_s].new(self).corrections end end end 07 & 3 3 * % * / (  5 ) &  & 3 3 0 3  . & 4 4 "( &
  47. * / 4 * % &  5 ) &

     * / * 5 * " - * ; & 3 module DidYouMean class ANameChecker include SpellCheckable def initialize(exception) # pull out the user input and generate # a dictionary using the exception object. end end end
  48. NameError#name begin doesnt_exist rescue NameError => error error.name # =>

    :doesnt_exist end begin DoesntExist rescue NameError => error error.name # => :DoesntExist end begin @@doesnt_exist rescue NameError => error error.name # => :@@doesnt_exist end
  49. NameError#name begin doesnt_exist("argument") rescue NoMethodError => error error.name # =>

    :doesnt_exist end begin self.doesnt_exist rescue NoMethodError => error error.name # => :doesnt_exist end
  50. * / 4 * % &  5 ) &

     * / * 5 * " - * ; & 3 module DidYouMean class ANameChecker include SpellCheckable def initialize(exception) @name = exception.name # user input @names = ???? # dictionary end end end
  51. . & 5 ) 0 %  / " .

    & 4 module DidYouMean class MethodNameChecker include SpellCheckable def initialize(no_method_error) # user input @name = no_method_error.name # dictionary @method_names = no_method_error.receiver.methods end end end
  52. module DidYouMean class MethodNameChecker include SpellCheckable def initialize(no_method_error) # user

    input @name = no_method_error.name # dictionary @method_names = no_method_error.receiver.methods end end end . & 5 ) 0 %  / " . & 4
  53. NameError#receiver R uby 2.3! string = "receiver" begin string.doesnt_exist rescue

    NameError => error error.receiver == string # => true end w 6TFEUPCFJNQMFNFOUFEBTB$FYUFOTJPO w *TOPXQBSUPG3VCZ w 3FUVSOTUIFSFDFJWFSXIFSFUIFNFUIPEJTDBMMFEPO
  54. . & 5 ) 0 %  / " .

    & 4 module DidYouMean class MethodNameChecker include SpellCheckable def initialize(no_method_error) # user input @name = no_method_error.name # dictionary @method_names = no_method_error.receiver.methods end end end
  55. . & 5 ) 0 %  / " .

    & 4 module DidYouMean class MethodNameChecker include SpellCheckable def initialize(no_method_error) # user input @name = no_method_error.name # dictionary @method_names = no_method_error.receiver.methods end def candidates { @name => @method_names } end end end
  56. 7" 3 * " # - &  / "

    . & 4 module DidYouMean class VariableNameChecker include SpellCheckable def initialize(name_error) # user input @name = name_error.name.to_s # dictionary r = name_error.receiver @method_names = r.methods + r.private_methods @ivar_names = r.instance_variables @cvar_names = r.class.class_variables @cvar_names += r.class_variables if r.kind_of?(Module) @lvar_names = name_error.local_variables end def candidates { @name => (@lvar_names + @method_names + @ivar_names + @cvar_names) } end end end
  57. 7" 3 * " # - &  / "

    . & 4 module DidYouMean class VariableNameChecker include SpellCheckable def initialize(name_error) # user input @name = name_error.name.to_s # dictionary r = name_error.receiver @method_names = r.methods + r.private_methods @ivar_names = r.instance_variables @cvar_names = r.class.class_variables @cvar_names += r.class_variables if r.kind_of?(Module) @lvar_names = name_error.local_variables end def candidates { @name => (@lvar_names + @method_names + @ivar_names + @cvar_names) } end end end
  58. 7" 3 * " # - &  / "

    . & 4 module DidYouMean class VariableNameChecker include SpellCheckable def initialize(name_error) # user input @name = name_error.name.to_s # dictionary r = name_error.receiver @method_names = r.methods + r.private_methods @ivar_names = r.instance_variables @cvar_names = r.class.class_variables @cvar_names += r.class_variables if r.kind_of?(Module) @lvar_names = name_error.local_variables end def candidates { @name => (@lvar_names + @method_names + @ivar_names + @cvar_names) } end end end
  59. $ - " 4 4  / " . &

    4 module DidYouMean class ClassNameChecker include SpellCheckable def initialize(exception) @class_name, @receiver = exception.name, exception.receiver end def candidates { @class_name => class_names } end # generates a dictionary def class_names scopes.flat_map do |scope| scope.constants.map do |constant| scope == Object ? constant : "#{scope}::#{constant}" end end end def scopes @receiver.to_s.split("::").inject([Object]) do |_scopes, scope| _scopes << _scopes.last.const_get(scope) end.uniq end end end
  60. module DidYouMean class ClassNameChecker include SpellCheckable def initialize(exception) @class_name, @receiver

    = exception.name, exception.receiver end def candidates { @class_name => class_names } end # generates a dictionary def class_names scopes.flat_map do |scope| scope.constants.map do |constant| scope == Object ? constant : "#{scope}::#{constant}" end end end def scopes @receiver.to_s.split("::").inject([Object]) do |_scopes, scope| _scopes << _scopes.last.const_get(scope) end.uniq end end end $ - " 4 4  / " . & 4 5-%3
  61. $ - " 4 4  / " . &

    4 ... class Person ... def address Address.new(raw_address) end class Address ... def zipcode ZipCode.new(raw_zipcode) # => NameError end class Zipcode ... end end end
  62. $ - " 4 4  / " . &

    4 ... class Person ... def address Address.new(raw_address) end class Address ... def zipcode ZipCode.new(raw_zipcode) # => NameError end class Zipcode ... end end end Object.constants Person.constants Address.constants
  63. $ - " 4 4  / " . &

    4 ... class Person ... def address Address.new(raw_address) end class Address ... def zipcode ZipCode.new(raw_zipcode) # => NameError end class Zipcode ... end end end + + Object.constants Person.constants Address.constants
  64. "  T Q F M M  D I

    F D L F S  . P O L F Z  Q B U D I F T ✅ ✅