Forum Discussion
Patrick2788
Jul 14, 2022Silver Contributor
A LAMBDA Word Search Puzzle
I've created a 15x10 Word Search. I've included a word list (dynamic range) containing words which might be found in the word search. The goal is to then extract those words when they appear in the...
- Jul 16, 2022
Here is one more variant
//------ variant without TEXTJOIN() reverseColumns= LAMBDA( data, LET( n, COLUMNS( data ), CHOOSECOLS( data, SEQUENCE(n,,n,-1) ) )); reversedText= LAMBDA( str, LET( head, LEFT(str), tail, RIGHT(str, LEN(str) - 1), IF(str = "", "", reversedText(tail) & head) )); isPolindrom= LAMBDA( text, text = reversedText( text ) ); checkSingleWord= LAMBDA( str, vector, LET( getWord, REDUCE("", vector, LAMBDA( a,v, IF( LEFT(str, LEN(a&v)) = a&v, a&v, IF( LEFT(str) = a, v, IF( a = str, a, "" ) ) ) ) ), IF( getWord = str, str, "") )); checkListOfWords= LAMBDA( wordsVector, vector, LET( getWords, REDUCE("", wordsVector, LAMBDA(a,v, VSTACK(a, checkSingleWord( v, vector) ) ) ), IFERROR( FILTER( getWords, getWords <> ""), "" ) )); wordsInMatrix= LAMBDA( data, wordsVector, LET( k, SEQUENCE( ROWS(data) ), scanData, REDUCE(1, k, LAMBDA(a,v, CHOOSECOLS( IF( v < SEQUENCE(,v,v,-1), a, VSTACK(a, checkListOfWords( Words, CHOOSEROWS(data,v) ) ) ), 1 ) )), removeFirst, DROP( scanData, 1 ), FILTER( removeFirst, removeFirst <> "") )); wordsInPuzzle= LAMBDA( data, wordsVector, LET( allWords, SORT( VSTACK( wordsInMatrix( data, wordsVector ), wordsInMatrix( reverseColumns( data ), wordsVector ), wordsInMatrix( TRANSPOSE( data ), wordsVector ), wordsInMatrix( reverseColumns( TRANSPOSE( data ) ), wordsVector ) )), ifPolindroms, MAP(allWords, LAMBDA(v, isPolindrom(v) ) ), polindroms, UNIQUE( FILTER(allWords, ifPolindroms)), notPolindroms, FILTER(allWords, ifPolindroms -1), stack, IF( ISERR(polindroms), notPolindroms, VSTACK( polindroms, notPolindroms ) ), SORT( stack ) ));
PeterBartholomew1
Jul 21, 2022Silver Contributor
Rather than listing the words found, I have identified them on copies of the puzzle.
Worksheet formula
= Solveλ(Puzzle, WordList, 0) +
Solveλ(Puzzle, WordList, 1)
Solveλ = LAMBDA(grid, findlist, d,
LET(
puzzleStr, Grid2Stringλ(grid,d),
extendedList, VSTACK(findlist, MAP(findlist, Reverseλ)),
pointer, MAP(extendedList, LocateWordλ(puzzleStr)),
ptr, FILTER(pointer,pointer),
wLen, FILTER(LEN(extendedList),pointer),
puzzleIdx, SEQUENCE(LEN(puzzleStr)),
n, IF(d, ROWS(grid), COLUMNS(grid)),
String2Gridλ(puzzleIdx,ptr,wLen,n,d)
)
);
Grid2Stringλ = LAMBDA(grid,d,
LET(
nRow, ROWS(grid),
nCol, COLUMNS(grid),
extendedGrid, EXPAND(grid, nRow+1, nCol+1, "|"),
CONCAT(TOCOL(extendedGrid,,d))
)
);
String2Gridλ = LAMBDA(k,wp,wl,n,d,
LET(
s, XLOOKUP(k,wp,wp,0,-1),
ℓ, XLOOKUP(k,wp,wl,0,-1),
b, SIGN(k+1-s <= ℓ),
ext, IF(d,
WRAPCOLS(b,n+1),
WRAPROWS(b,n+1)),
DROP(ext,-1,-1)
)
);
LocateWordλ = LAMBDA(puzzleStr, LAMBDA(word,
IFERROR(FIND(word, puzzleStr),0))
);
Reverseλ = LAMBDA(word,
LET(n, LEN(word), k, SEQUENCE(n, 1, n, -1), CONCAT(MID(word, k, 1)))
);
Something else that occurs to me is that your recursion technique might be the only way forward if the standard across and down wordsearch had words that change direction like a game of 'snake'.
Patrick2788
Jul 22, 2022Silver Contributor
This is a very elegant solution and you delivered on the ideas you suggested last week. This is an interesting exercise because there's multiple ways to manipulate the matrix to locate the words. It looks like you took the approach that it's better to locate the words in a vector than a matrix. If I follow correctly, you converted the matrix to a vector, located the words, and then ultimate wrapped it back into a matrix. Very cool!
I have some general observations:
-I typically use REDUCE and some sequencing to reverse text. I noticed you used SEQUENCE with your LAMBDA. SergeiBaklan utilized some clever recursion to reverse text. I'm trying to get a feel for when to use recursion. My guess is recursion might calculate a bit slower but for a small data set the difference is negligible.
-This may be a matter of style but I'm curious why LAMBDA is nested here. I removed one LAMBDA and noticed it still calculates correctly:
LocateWordλ = LAMBDA(puzzleStr, LAMBDA(word,
IFERROR(FIND(word, puzzleStr),0))
- PeterBartholomew1Jul 23, 2022Silver Contributor
So many points to answer! Your introductory paragraph captures the intent of the solution perfectly. I also used the fact that the number returned for a character in the string corresponds to its sequence number in the grid.
With few exceptions, I have consigned recursion to the past, since I find it intensely difficult to construct solutions or explain them and, for preference, I use the most appropriate Lambda helper function. I suspect, but have not demonstrated, that the helper functions improve the computational efficiency and are they are not constrained by size of the parameter stack. Something that recursion can do that SCAN (say) cannot, is terminate the calculation according to a criterion that is not known at the outset (convergence to some tolerance, for example). SCAN would continue processing nothing very much until it reached the end of the scanned array. TAKE, DROP or FILTER could be used to 'trim' the result.
Whether the LAMBDA is in Curried form (one parameter fed at a time to nested functions) or as a single multi-argument function is of little importance (they are equivalent), until you come to use it within a Lambda helper function that will expect a specific signature in terms of the parameters it passes. Currying allows the leftmost arguments to be provided explicitly by the developer whilst the final arguments are provided by the helper function. Maybe the subject would be worthy of a uTube clip in its own right?
- SergeiBaklanJul 24, 2022MVP
I could agree that built into SCAN and REDUCE recursion shall be faster than manual recursion. However, didn't do any test and have no idea how dramatical the difference is and are there differences in stack limits.
Without that the only point to use this or that is do we have ready to use patterns or not.