Search code examples
prolog

Determine if the string contains numeric characters. If there are, then display them on the screen


I don't understand why it doesn't work. I need to read the user's string and check if there are numbers, if so print them. I seem to have done this, but it won't work and I don't understand what's wrong...

enter image description here

word_digits( string, string ).
present_digits(string).

present_digits("") :- fail.
  present_digits( S ) :-
    frontchar( S, C, _ ),
    C>='0',C<='9',!.

present_digits( S ) :-
    frontchar( S, _, S2 ),
    present_digits(S2).
    
word_digits( "", "" ) :- !.

word_digits( S, O ) :-
    fronttoken( S, T, S2 ),
    present_digits(T),
    word_digits( S2, O2 ),
    concat( T, " ", O3 ),
    concat( O3, O2, O ), !.

word_digits( S, O ) :-
    fronttoken( S, _, S2 ),
    word_digits( S2, O ).


printThatShit:-
    write( "Enter string: " ),
    read(S),  nl,
    word_digits( S, Digits ),
    write(Digits).

Solution

  • Your types are all over the place. In SWI-Prolog (which you are using) there are atoms, for example foo and 'Another atom', and moreover:

    • character codes (Unicode numerical value for the character) and a list of those:
    ?- atom_codes(foo, Codes).
    Codes = [102, 111, 111].
    
    • chars (one-letter atoms) and a list of that
    ?- atom_chars(foo, Chars).
    Chars = [f, o, o].
    
    • strings, which are a memory-efficient way of dealing with text
    ?- atom_string(foo, String).
    String = "foo".
    

    If you solved that part in your code you will be much further than now.

    Another hint: read reads a Prolog term. If you want to just read some text, you need to read codes, chars, or an SWI-Prolog string. See read_line_to_codes or read_line_to_string or read_string etc. Once you decide on the data type you can decide on how to get the digits (or numbers?) out: DCG, regexp, just simple filter on a list?


    Assuming you already have a working solution (you should actually post it as an answer!)

    This is one way to do it using a DCG:

    :- use_module(library(dcg/basics)).
    
    line_digits([D|Ds]) --> digit(D), !,
        line_digits(Ds).
    line_digits([]) --> "\n", !.
    line_digits(Ds) --> [_],
        line_digits(Ds).
    
    only_digits -->
        { format("Enter string: "), flush },
        line_digits(Ds),
        { format("~s~n", [Ds]) },
        !,
        only_digits.
    only_digits --> eos.
    

    To run it:

    ?- phrase_from_stream(only_digits, user_input).
    Enter string: this is a string
    
    Enter string: 234 this is another 3243 string999
    2343243999
    Enter string: ^D
    true.
    

    You stop it by typing Ctrl+D.

    Quite a bit of code is needed to do the read-print loop. Maybe there is a cleaner way.

    If you only had a list of say chars (one-character atoms) then this would be an easy way to do it:

    only_digits(String, Digits) :-
        include(digit, String, Digits).
    
    digit(C) :- char_type(C, digit).
    

    To use it:

    ?- only_digits([a,b,c,'1','2','3',x,y], Ds).
    Ds = ['1', '2', '3'].