1

So basically I am trying to use some Prolog code to simulate pointer like behavior.

I asked a related question here, and after around one month, I finally have time to start.

Here is a simple example in C:

int a = 1;
int* p = &a;
int b = *p;   

And I want to translate this code into Prolog like this (or other better strategies?):

A is 1, 
assert(ref(p, a)),    <-  this can be dynamic fact gene
ref(p, TEMP),   <-  now I want to use a!!
to_lowercase(TEMP, TEMP1),  <- I don't know how to implement to_low
B is TEMP1.     <- reflection? 

So in the above code, I am confused with

  1. In my understanding, after ref(p, TEMP), then TEMP will equal to "a", and it is just a string, then how can I reuse it as a variable name, sounds like a reflection...?

  2. How to implement the to_lowercase function?

Am I clear?

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
lllllllllllll
  • 8,519
  • 9
  • 45
  • 80
  • 1
    Could you give an example of what you are trying to achieve? Why do you state `A is 1` if you never use `A`? Why are you lower_casing a lowercase letter? Are you trying to implement `to_lowercase/2` or just trying to find the right library predicate? Prolog is [Homoiconic](https://en.wikipedia.org/wiki/Homoiconicity), which "makes metaprogramming easier ... since code can be treated as data: reflection in the language ... depends on a single, homogeneous structure, and it does not have to handle several different structures that would appear in a complex syntax." – Shon May 13 '14 at 05:10

3 Answers3

5

If you are really that determined to simulate a computer from within Prolog, you should take into account the answers to your previous questions before moving on. Either way, this answer makes a lot of assumptions about what your ultimate goal is. I am guessing that you are trying to simulate a machine, and write a simulator that takes source code written in a C-style language and executes it.

So let's say you have a very simple processor with flat memory space (some small embedded microcontrollers are like this). Your whole memory then would be just one chunk of 16-bit addresses, let's say 1000 of them:

functor(Memory, memory, 1000).

Taking your C code above, a compiler might come up with:

  • Pick an address Addr1 for a, which is an int, and write the value 1 at that address
  • Pick an address Addr2 for p, which is an int *, and write the value of the address of a at that address
  • Pick an address Addr3 for b, which is an int, and write to it the value which is at the memory to which the value in p is pointing to.

This could translate to machine code for the machine you are simulating (assuming the actual addresses have been already picked appropriately by the compiler):

arg(Addr1, Memory, 1),     % int a = 1;
arg(Addr2, Memory, Addr1), % int *p = &a;
arg(Addr2, Memory, Tmp1),  %% put the value at p in Tmp1; this is an address
arg(Tmp1, Memory, Tmp2),   %% read the value at the address Tmp1 into Tmp2
arg(Addr3, Memory, Tmp2).  % int b = *p;

So of course all addresses should be within your memory space. Some of the calls to arg/3 above are reads and some are writes; it should be clear which is which. Note that in this particular conjunction all three arguments to the term memory/1000 are still free variables. To modify the values at an address that has been already set you would need to copy it accordingly, freeing the address you need to reuse.

Please read carefully all the answers to your questions before pressing on.

  • Sorry for interrupt again, I am wrong, SSA might not help in this issue, then basically how to free the bounding based on this? Do I need to use `assert` and `retract`..? – lllllllllllll May 13 '14 at 16:30
  • `assert` and `retract` is doable, but unnecessary. You have two other choices: 1. Copy all arguments of the term but the one you need to free to a new term (shown in previous answers to your questions); or 2. use global variables (also shown in previous answers). I would prefer the first, since there is not reason not to do it. –  May 13 '14 at 17:08
  • You mean like a `copy_term/2`? That should be doable, but IMHO, I have to use a new name for the "Memory", and as I can leverage "while" in the simulation, then probably I need to use a counter and assign a new name after every "Memory copy", Am I right? Is there any better solution that can don't need to generate a new name each time? – lllllllllllll May 13 '14 at 21:13
  • @computereasy I actually meant the approach as in this answer: http://stackoverflow.com/questions/22598583/how-to-simulate-add-eax-1-in-prolog/22602449#22602449 I don't promise it's the best way to do it but my feeling says try that first –  May 14 '14 at 04:15
4

You need to read a good book on Prolog. I'd suggest The Art of Prolog.

Prolog doesn't have anything like pointers, or addresses or variables. It's got terms. An unbound term is variable because it's not yet bound. Once bound (unified), it ceases to be variable and it becomes that with which it unified. It cannot be assigned a new value — unless the unification is undone via backtracking and an alternative path taken. Hence the term unification.

Trying to map the concept of pointers and memory addresses onto prolog is somewhat akin to putting fish gills on a bicycle.

As far as implementing a predicate for converting a strong to lower-case, you should realize that Prolog doesn't really have strings: the Prolog string "ABC" is exactly identical to the list [65,66,67], a a list of integers representing ASCII/Unicode code points. It is what is called syntactic sugar. So...given that identity...

Something like

to_lower( [] , [] ).
to_lower( [C|Cs] , [L|Ls] ) :-
  char_code('A',A),
  char_code('Z',Z),
  C >= A ,
  C =< Z ,
  ! , 
  char_code(a,Base),
  Offset = C - A ,
  L is Base+Offset,
  to_lower(Cs,Ls).
to_lower([C|Cs],[C|Ls]) :-
  to_lower(Cs,Ls).

Should do you.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
3

Since you tag the question SWI-Prolog, I assume you have clear that the string concept has undergone some important change in recent times, mainly for efficiency reasons.

Look at downcase_atom/2, or string_lower/2, depending on your intended usage (I linked to string processing page, because the string_lower one has a typo).

For storing 'pointers' like objects, I suggest to use global variables, nb_setval/2, nb_getval/2, nb_current/2 instead of assert/retract. For first, are much more efficient (I measured time ago a factor of 3 in favour of nb_ predicate family), and make clearer the intended usage. assert/retract are better used to update a dynamic knowledge base.

CapelliC
  • 59,646
  • 5
  • 47
  • 90