If you are not allowed to import anything, then the excellent reduce operation along with slicing and zip (all these are Python built-in modules that do not require import) can be a very compact way:
EDIT After he pointed out to me that I misunderstood the problem, fixed it by changing the zip() statement.
And the output of the result (also edited )
print result {'and': ['more'], 'direction': ['implies'], 'implies': ['dimension', 'direction', 'measurement'], 'less': ['the'], 'measurement':['implies'], 'the': ['implies', 'and'], 'more': ['the']}
For completeness, the old, erroneous result:
print result {'the': ['and'], 'implies': ['dimension', 'direction', 'measurement'], 'more': ['the']}
Little explanation
After dividing the string into a list of words, we can index individual words as words[i] .
edited . In the statement of the problem, the keys of the resulting dict are the words following the word, and this value is the first word. Therefore, we must convert the list of words into a list of combinations of each word with the next word. Thus, the key list will be a list of [words [1], words [2], words [3], ....] and values that go with them: [words [0], words [1], words [2 ], ..., the words [n-1]].
Using Python slicing : keys = words[1:] and values = words[:-1]
Now we need to create a dict these keys and values, aggregating the values โโin list , if the same key happens several times.
A dict has a .setdefault(key, value) method that initializes the value of key to value if key not in the dict , otherwise it returns the value as it currently is. By default, initializing all values โโto empty list ( [] ), we can blindly call .append(...) on it. What this part of the code does:
acc.setdefault(key, []).append( value )
Then there is reduce . The decrease operation reduces (...) the list of values โโinto one. In this case, we will shorten the list of (key, value) tuples in the dict , where we copied all the values โโinto their corresponding key.
reduce accepts a callback reduction function and an initial element. The starting element here is an empty dict {} - we will fill this out when we go.
The callback reduction function is called several times with two arguments, a battery and the next element to add to the accumulation. The function should return a new battery.
In this code, the recovery step basically consists in adding the item value to the list of values โโfor the item key. (See above - what .setdefault().append() does).
We only need to get a list of tuples (key, value) that we need to process. This is where the built-in zip appears. zip takes two lists and returns a list of tuples of the corresponding elements.
In this way:
zip(words[1:], words[:-1])
prints exactly what we want: a list of all tuples (key, value) .
Finally, since the decrease function should bring back a new battery, we have to play the trick. list.append(...) returns None , although the actual dict has been changed. Therefore, we cannot return this value as the next battery. So, after that we add the construction or acc .
Since the left side of a logical or always evaluated as None , which is logically False in Python, the right side is always "evaluated" - in this case, the (modified) dict itself. Thus, a pure or result is evaluated by the modified dict itself, which is exactly what we need to return.